--- loncom/interface/statistics/lonsurveyreports.pm 2004/07/06 15:56:42 1.2 +++ loncom/interface/statistics/lonsurveyreports.pm 2008/10/23 09:07:51 1.18 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonsurveyreports.pm,v 1.2 2004/07/06 15:56:42 matthew Exp $ +# $Id: lonsurveyreports.pm,v 1.18 2008/10/23 09:07:51 bisitz Exp $ # # Copyright Michigan State University Board of Trustees # @@ -27,23 +27,29 @@ package Apache::lonsurveyreports; use strict; -use Apache::lonnet(); +use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon(); use Apache::loncoursedata(); use Apache::lonstatistics; use Apache::lonlocal; use Apache::lonstathelpers; +use Spreadsheet::WriteExcel; use HTML::Entities(); use Time::Local(); +use lib '/home/httpd/lib/perl/'; +use LONCAPA; + -my @SubmitButtons = ({ name => 'PrevProblem', +my @SubmitButtons = ( + { name => 'break'}, + { name => 'PrevProblem', text => 'Previous Survey' }, { name => 'NextProblem', text => 'Next Survey' }, - { name => 'break'}, { name => 'SelectAnother', - text => 'Choose a different Survey Problem' }, + text => 'Choose a different Survey' }, + { name => 'break'}, { name => 'Generate', text => 'Generate Report'}, ); @@ -67,15 +73,16 @@ sub BuildSurveyReportsPage { my @Students = @Apache::lonstatistics::Students; # if (@Students < 1) { - $r->print('

There are no students in the sections selected

'); + $r->print('

'.&mt('There are no students in the sections selected.').'

'); } # my @CacheButtonHTML = &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status'); $r->rflush(); # - if (exists($ENV{'form.problemchoice'}) && - ! exists($ENV{'form.SelectAnother'})) { + if (exists($env{'form.problemchoice'}) && + ! exists($env{'form.SelectAnother'})) { + $r->print(' 'x3); foreach my $button (@SubmitButtons) { if ($button->{'name'} eq 'break') { $r->print("
\n"); @@ -90,42 +97,56 @@ sub BuildSurveyReportsPage { } # $r->print('
'); + $r->print('

'. + &Apache::lonlocal::locallocaltime(time).','. + &Apache::lonstatistics::section_and_enrollment_description(). + '

'); $r->rflush(); # # Determine which problem we are to analyze my $current_problem = &Apache::lonstathelpers::get_target_from_id - ($ENV{'form.problemchoice'}); + ($env{'form.problemchoice'}); # - my ($prev,$curr,$next) = + my ($navmap,$prev,$curr,$next) = &Apache::lonstathelpers::get_prev_curr_next($current_problem, '.', 'part_survey', ); - if (exists($ENV{'form.PrevProblem'}) && defined($prev)) { + if (exists($env{'form.PrevProblem'}) && defined($prev)) { $current_problem = $prev; - } elsif (exists($ENV{'form.NextProblem'}) && defined($next)) { + } elsif (exists($env{'form.NextProblem'}) && defined($next)) { $current_problem = $next; } else { $current_problem = $curr; } # # Store the current problem choice and send it out in the form - $ENV{'form.problemchoice'} = + $env{'form.problemchoice'} = &Apache::lonstathelpers::make_target_id($current_problem); $r->print(''); + $env{'form.problemchoice'}.'" />'); # if (! defined($current_problem->{'resource'})) { $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->print('

'.$resource->compTitle.'

'); + $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'}); - &make_HTML_report($r,$current_problem,\%Data,\@Students); + ($resource->src); + &compile_student_answers($r,$current_problem,\%Data,\@Students); + if ($env{'form.output'} eq 'HTML' || + ! defined($env{'form.output'})) { + &make_HTML_report($r,$current_problem,\%Data,\@Students); + } elsif ($env{'form.output'} eq 'Excel') { + &make_Excel_report($r,$current_problem,\%Data,\@Students); + } elsif ($env{'form.output'} eq 'TXT') { + &make_text_report($r,$current_problem,\%Data,\@Students); + } } $r->print('
'); } else { @@ -147,12 +168,13 @@ sub BuildSurveyReportsPage { sub SurveyProblemSelector { my $Str = ''; my @SurveyProblems; - foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess('all')) { - next if ($seq->{'num_assess'}<1); - foreach my $res (@{$seq->{'contents'}}) { - next if ($res->{'type'} ne 'assessment'); - foreach my $part (@{$res->{'parts'}}) { - if ($res->{'partdata'}->{$part}->{'Survey'}) { + my ($navmap,@sequences) = + &Apache::lonstatistics::selected_sequences_with_assessments('all'); + foreach my $seq (@sequences) { + my @resources = &Apache::lonstathelpers::get_resources($navmap,$seq); + foreach my $res (@resources) { + foreach my $part (@{$res->parts}) { + if ($res->is_survey($part)) { push(@SurveyProblems,{res=>$res,seq=>$seq,part=>$part}); last; } @@ -160,32 +182,36 @@ sub SurveyProblemSelector { } } if (! scalar(@SurveyProblems)) { - $Str = '

'. - &mt('There are no survey problems in this course'). - '

'.$/; + $Str = '

'. + &mt('There are no survey problems in this course.'). + '

'.$/; return $Str; } $Str .= ''.$/; $Str .= ''.''. - ''. - ''. + ''. ''.$/; + my $id; foreach my $problem (@SurveyProblems) { + $id++; my $value = &Apache::lonstathelpers::make_target_id - ({symb=>$problem->{'res'}->{'symb'}, + ({symb=>$problem->{'res'}->symb, part=>$problem->{'part'}, respid=>undef, resptype=>undef}); my $checked = ''; - if ($ENV{'form.problemchoice'} eq $value) { + if ($env{'form.problemchoice'} eq $value) { $checked = 'checked '; } - $Str .= ''.''. - ''. - ''. - ''.$/; + ''.$/; } $Str .= '
'.&mt('Sequence').''.&mt('Problem').''.&mt('Survey').'
'. - '{'res'}->link. + '?symb='.&escape($problem->{'res'}->shown_symb); + $Str .= '
'. + ''.''.$problem->{'seq'}->{'title'}.''.$problem->{'res'}->{'title'}.'
'. + ''. + (' 'x2). + ''.&mt('View survey').''.'
'; return $Str; @@ -198,37 +224,47 @@ sub SurveyProblemSelector { ## ######################################################### ######################################################### -sub Compile_Student_Answers { - my ($problem,$ProblemData,$Students) = @_; +sub compile_student_answers { + my ($r,$problem,$ProblemData,$Students) = @_; my $resource = $problem->{'resource'}; + my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin + ($r,'Processing Student Submissions', + 'Processing Student Submissions', + scalar(@$Students),'inline',undef,'Statistics','stats_status'); foreach my $student (@$Students) { - foreach my $partid (@{$resource->{'parts'}}) { - my $partdata = $resource->{'partdata'}->{$partid}; - for (my $i=0;$i<=@{$partdata->{'ResponseIds'}};$i++) { - my $respid = $partdata->{'ResponseIds'}->[$i]; - my $resptype = $partdata->{'ResponseTypes'}->[$i]; + foreach my $partid (@{$resource->parts}) { + my @response_ids = $resource->responseIds($partid); + my @response_types = $resource->responseType($partid); + for (my $i=0;$i<=$#response_ids;$i++) { + my $respid = $response_ids[$i]; + my $resptype = $response_types[$i]; my $results = &Apache::loncoursedata::get_response_data_by_student - ($student,$resource->{'symb'},$respid); + ($student,$resource->symb,$respid); next if (! defined($results) || ref($results) ne 'ARRAY' || ref($results->[0]) ne 'ARRAY'); my $student_response = $results->[0]->[&Apache::loncoursedata::RDs_submission()]; $problem->{'responsedata'}->{$partid}->{$respid}->{'_count'}++; my $data = $problem->{'responsedata'}->{$partid}->{$respid}; - if ($resptype =~ /^(radiobutton|optionresponse)$/) { - # Restricted response type can be categorized. - # - # Assume responses were not randomized and the order - # represents their value. This is probably a dumb thing - # to do... - # - my ($foil,$value) = split('=',$student_response); + if ($resptype =~ /^(option|match)$/) { + my @responses = split('&',$student_response); + foreach my $response (@responses) { + my ($foilid,$option) = + map { + &unescape($_); + } split('=',$response); + $data->{'foil_count'}->{$foilid}++; + $data->{'foil_responses'}->{$foilid}->{$option}++; + } + } elsif ($resptype =~ /^(radiobutton)$/) { + my ($foil,$value) = map { &unescape($_); } split('=',$student_response); $value += 1; # explicitly increment it... $data->{'foil_responses'}->{$foil}++; $data->{'foil_values'}->{$value}++; if (! exists($data->{'map'}->{$value})) { $data->{'map'}->{$value} = $foil; + $data->{'map_fv'}->{$foil} = $value; } } else { # Variable stuff (essays, raw numbers, strings) go here @@ -236,7 +272,190 @@ sub Compile_Student_Answers { } } } + &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, + 'last student'); } + &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); + return; +} + + +######################################################### +######################################################### +## +## make_text_report +## +######################################################### +######################################################### +sub make_text_report { + my ($r,$problem,$problem_data,$students) = @_; + my ($file,$filename) = &Apache::loncommon::create_text_file($r,'txt'); + if (! defined($file)) { return '';} + $r->print(''); + my $resource = $problem->{'resource'}; + print $file $resource->compTitle.$/; + print $file &Apache::lonstatistics::section_and_enrollment_description(). + ' '.&mt('Generated on [_1]',&Apache::lonlocal::locallocaltime(time)). + $/; + my $something_has_been_output = 0; + foreach my $partid (@{$resource->parts}) { + my @response_ids = $resource->responseIds($partid); + my @response_types = $resource->responseType($partid); + for (my $i=0;$i<=$#response_ids;$i++) { + my $respid = $response_ids[$i]; + my $resptype = $response_types[$i]; + my $data = $problem->{'responsedata'}->{$partid}->{$respid}; + if (exists($data->{'responses'}) && + ref($data->{'responses'}) eq 'ARRAY') { + # Essay type response + print $file ('-'x40).$/; + print $file + $resource->part_display($partid).', '.$respid.':'.$resptype.$/; + foreach my $submission (@{$data->{'responses'}}) { + print $file ('-'x20).$/; + $submission =~ s/(\\r\\n|\\n)/\n/g; + $submission =~ s/\\(\'|\"|\`)/$1/g; + print $file $submission.$/.$/; + $something_has_been_output=1; + } + } + } + } + close($file); + if($something_has_been_output) { + $r->print('

'. + &mt('Your text file'). + '

'."\n"); + $r->print(''); + } else { + $r->print('

' + .&mt('There is no essay or string response data to output for this survey.') + .''); + } + $r->rflush(); + return; +} + + +######################################################### +######################################################### +## +## make_Excel_report +## +######################################################### +######################################################### +sub make_Excel_report { + my ($r,$problem,$problem_data,$students) = @_; + my ($workbook,$filename,$format) = &Apache::loncommon::create_workbook($r); + if (! defined($workbook)) { return '';} + $r->print(''); + my $worksheet = $workbook->addworksheet('Survey Reports'); + # + my $rows_output=0; + $worksheet->write($rows_output++,0, + $env{'course.'.$env{'request.course.id'}.'.description'}, + $format->{'h1'}); + $rows_output++; + # + my $resource = $problem->{'resource'}; + $worksheet->write($rows_output++,0,$resource->compTitle,$format->{'h2'}); + foreach my $partid (@{$resource->parts}) { + my @response_ids = $resource->responseIds($partid); + my @response_types = $resource->responseType($partid); + for (my $i=0;$i<=$#response_ids;$i++) { + my $respid = $response_ids[$i]; + my $resptype = $response_types[$i]; + my $data = $problem->{'responsedata'}->{$partid}->{$respid}; + my $cols_output=0; + $worksheet->write($rows_output,$cols_output++, + $resource->part_display($partid),$format->{'h3'}); + $worksheet->write($rows_output,$cols_output++, + 'Response '.$respid.', '.$resptype, + $format->{'h3'}); + $rows_output++; + if (exists($data->{'responses'}) && + ref($data->{'responses'}) eq 'ARRAY') { + my $warned_about_size = 0; + foreach my $data (@{$data->{'responses'}}) { + if (length($data) > 255 && ! $warned_about_size) { + $r->print('

'. + &mt('[_1]:[_2] responses to [_3] may be too long to fit Excel spreadsheet.', + $resource->compTitle, + $resource->part_display($partid), + $respid). + '

'); + $r->rflush(); + $warned_about_size=1; + } + $worksheet->write($rows_output++,0,$data); + } + } elsif (exists($data->{'foil_count'}) && + exists($data->{'foil_responses'})) { + my $respdata = $problem_data->{$partid.'.'.$respid}; + my @rowdata = ('Foil Name','Foil Text','Option', + 'Frequency'); + $worksheet->write_row($rows_output++,0, + \@rowdata,$format->{'h4'}); + # + my @foils = sort(keys(%{$respdata->{'_Foils'}})); + foreach my $foilid (@foils) { + my $foil_count = $data->{'foil_count'}->{$foilid}; + my $foiltext = $respdata->{'_Foils'}->{$foilid}->{'text'}; + my $foilname = $respdata->{'_Foils'}->{$foilid}->{'name'}; + $foiltext = &HTML::Entities::decode($foilname); + my $cols_output=0; + $worksheet->write($rows_output,$cols_output++,$foilname); + $worksheet->write($rows_output,$cols_output++,$foiltext); + my $option_start_col = $cols_output; + # + foreach my $option (sort(@{$respdata->{'_Options'}})){ + $cols_output= $option_start_col; + $worksheet->write($rows_output,$cols_output++, + $option); + my $count= + $data->{'foil_responses'}->{$foilid}->{$option}; + $worksheet->write($rows_output,$cols_output++,$count); + $rows_output++; + } + } + } elsif (exists($data->{'_count'}) && + exists($data->{'foil_values'}) && + exists($data->{'map'})) { + my $respdata = $problem_data->{$partid.'.'.$respid}; + my @rowdata = ('Foil Name','Foil Text','Frequency'); + $worksheet->write_row($rows_output++,0, + \@rowdata,$format->{'h4'}); + my @foils = sort(keys(%{$respdata->{'_Foils'}})); + foreach my $foilid (@foils) { + undef(@rowdata); + my $value = $data->{'map_fv'}->{$foilid}; + push(@rowdata,$respdata->{'_Foils'}->{$foilid}->{'name'}); + push(@rowdata,$respdata->{'_Foils'}->{$foilid}->{'text'}); + push(@rowdata,$data->{'foil_values'}->{$value}); + $worksheet->write_row($rows_output++,0,\@rowdata); + } + } + $rows_output++; + } #response ids + } # partids + $workbook->close(); + $r->print('

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

'."\n"); + $r->print(''); + $r->rflush(); return; } @@ -249,17 +468,18 @@ sub Compile_Student_Answers { ######################################################### sub make_HTML_report { my ($r,$problem,$ProblemData,$Students) = @_; - &Compile_Student_Answers($problem,$ProblemData,$Students); - # &output_hash('',$ProblemData); my $resource = $problem->{'resource'}; - foreach my $partid (@{$resource->{'parts'}}) { - my $partdata = $resource->{'partdata'}->{$partid}; - for (my $i=0;$i<=@{$partdata->{'ResponseIds'}};$i++) { + foreach my $partid (@{$resource->parts}) { + my @response_ids = $resource->responseIds($partid); + my @response_types = $resource->responseType($partid); + for (my $i=0;$i<=$#response_ids;$i++) { my $Str = ''.$/; - my $respid = $partdata->{'ResponseIds'}->[$i]; - my $resptype = $partdata->{'ResponseTypes'}->[$i]; + my $respid = $response_ids[$i]; + my $resptype = $response_types[$i]; my $data = $problem->{'responsedata'}->{$partid}->{$respid}; - next if (! defined($data) || ref($data) ne 'HASH'); + if (! defined($data) || ref($data) ne 'HASH') { + next; + } # Debugging code # $Str .= ''. # ''. @@ -269,8 +489,9 @@ sub make_HTML_report { $Str .= ''. ''. ''. - ''. - ''.$/; + ''. + ''; if (exists($data->{'responses'}) && ref($data->{'responses'}) eq 'ARRAY') { &randomize_array($data->{'responses'}); @@ -284,6 +505,45 @@ sub make_HTML_report { '
'. ''.$/; } + } elsif (exists($data->{'foil_count'}) && + exists($data->{'foil_responses'})) { + $Str.='' + .'' + .&Apache::loncommon::end_data_table_header_row(); + my @foils = sort(keys(%{$ProblemData->{$partid.'.'.$respid}->{'_Foils'}})); + foreach my $foilid (@foils) { + my $prob_data = $ProblemData->{$partid.'.'.$respid}; + my $foil_count = $data->{'foil_count'}->{$foilid}; + my $foiltext = $prob_data->{'_Foils'}->{$foilid}->{'text'}; + my $foilname = $prob_data->{'_Foils'}->{$foilid}->{'name'}; + my $rowspan = scalar(@{$prob_data->{'_Options'}}); + my $preamble = &Apache::loncommon::start_data_table_row(). + ''. + ''; + foreach my $option (sort(@{$prob_data->{'_Options'}})){ + my $count = + $data->{'foil_responses'}->{$foilid}->{$option}; + $tmp .= $preamble. + ''. + ''. + ''.&Apache::loncommon::end_data_table_row().$/; + $preamble = &Apache::loncommon::continue_data_table_row(); #&Apache::loncommon::start_data_table_row(); + } + } + $Str.=$tmp.&Apache::loncommon::end_data_table() + .''; } elsif (exists($data->{'_count'}) && exists($data->{'foil_values'}) && exists($data->{'map'})) { @@ -291,29 +551,30 @@ sub make_HTML_report { my $total = $data->{'_count'}; my $sum = 0; my $tmp; - foreach my $value (sort(keys(%{$data->{'foil_values'}}))) { + my @foils = sort(keys(%{$ProblemData->{$partid.'.'.$respid} + ->{'_Foils'}})); + foreach my $foilid (@foils) { + my $value = $data->{'map_fv'}->{$foilid}; my $count = $data->{'foil_values'}->{$value}; - my $foilid = $data->{'map'}->{$value}; my $foiltext = $ProblemData->{$partid.'.'.$respid}->{'_Foils'}->{$foilid}->{'text'}; my $foilname = $ProblemData->{$partid.'.'.$respid}->{'_Foils'}->{$foilid}->{'name'}; - $sum = $value * $data->{'foil_values'}->{$value}; - $tmp .= ''. + $tmp .= &Apache::loncommon::start_data_table_row(). ''. ''. ''. ''. - ''.$/; + &Apache::loncommon::end_data_table_row().$/; } - $Str .= ''. + $Str .= &Apache::loncommon::start_data_table_row(). ''. ''. ''. ''. - ''.$/. + &Apache::loncommon::end_data_table_row().$/. $tmp; } - $Str.= '
'.$partid.'
'.&mt('Total').''.$data->{'_count'}.''.&mt('Part [_1], Response [_2]',$partid,$respid).'
'.&mt('Part [_1], Response [_2]', + $resource->part_display($partid),$respid).'
' + .&Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table_header_row(); + my $tmp = ''.join('', + (&mt('Foil Name'), + &mt('Foil Text'), + &mt('Option'), + &mt('Frequency'), + &mt('Percent'))).''. + $foilname.''. + $foiltext.''.$option.''.$count.''. + sprintf('%.2f',100*$count/$foil_count).'%'. + '
'.$foilname.''.$foiltext.''.$count.''. sprintf("%.2f",$count/$total*100).'%
'.&mt('Foil Name').''.&mt('Text').''.&mt('Freq').''.&mt('Percent').'

'; + $Str.= &Apache::loncommon::end_data_table().'
'; $r->print($Str); $r->rflush(); } @@ -342,36 +603,71 @@ sub CreateInterface { ## ## Environment variable initialization my $Str = ''; - $Str .= &Apache::lonhtmlcommon::breadcrumbs - (undef,'Student Submission Reports'); + my $output_selector = ''.$/; + $Str .= &Apache::lonhtmlcommon::breadcrumbs('Student Submission Reports'); $Str .= '

'; $Str .= ''."\n"; $Str .= ''; - $Str .= ''; - $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= ''; $Str .= ''."\n"; # $Str .= ''; # + $Str .= ''; + # $Str .= ''; # + $Str .= ''; + # + # Render problem checkbox + my $prob_checkbox = ''. + &mt('Show problem [_1]',$prob_checkbox).'
'. + ''."\n"; $Str .= '
'.&mt('Sections').''.&mt('Enrollment Status').''.&mt('Sections').''.&mt('Groups').''.&mt('Access Status').''.&mt('Output Format').''.' '.'
'."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ''."\n"; + $Str .= &Apache::lonstatistics::GroupSelect('Group','multiple',5); + $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= ''.$output_selector.'
'."\n"; # - $Str .= ''.&mt('Status: [_1]', - ''). - ''.'

'; + $Str .= '

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

'; + $Str .= '

'; ## return $Str; } - - 1; __END__