# The LearningOnline Network with CAPA # # $Id: lonsurveyreports.pm,v 1.4 2005/03/10 00:23:15 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::lonsurveyreports; use strict; 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(); my @SubmitButtons = ({ name => 'PrevProblem', text => 'Previous Survey' }, { name => 'NextProblem', text => 'Next Survey' }, { name => 'break'}, { name => 'SelectAnother', text => 'Choose a different Survey Problem' }, { name => 'Generate', text => 'Generate Report'}, ); sub BuildSurveyReportsPage { my ($r,$c)=@_; # my %Saveable_Parameters = ('Status' => 'scalar', 'Section' => 'array', 'NumPlots' => 'scalar', ); &Apache::loncommon::store_course_settings('survey_reports', \%Saveable_Parameters); &Apache::loncommon::restore_course_settings('survey_resports', \%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'); $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 problem we are to analyze my $current_problem = &Apache::lonstathelpers::get_target_from_id ($ENV{'form.problemchoice'}); # my ($navmap,$prev,$curr,$next) = &Apache::lonstathelpers::get_prev_curr_next($current_problem, '.', 'part_survey', ); if (exists($ENV{'form.PrevProblem'}) && defined($prev)) { $current_problem = $prev; } 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'} = &Apache::lonstathelpers::make_target_id($current_problem); $r->print(''); # if (! defined($current_problem->{'resource'})) { $r->print('resource is undefined'); } else { my $resource = $current_problem->{'resource'}; $r->print('

'.$resource->compTitle.'

'); $r->print('

'.$resource->src.'

'); $r->print(&Apache::lonstathelpers::render_resource($resource)); $r->rflush(); my %Data = &Apache::lonstathelpers::get_problem_data ($resource->src); 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); } } $r->print('
'); } else { $r->print(''); $r->print(' 'x5); $r->print('

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

'); $r->print(&SurveyProblemSelector()); } } ########################################################## ########################################################## ## ## SurveyProblemSelector ## ########################################################## ########################################################## sub SurveyProblemSelector { my $Str = ''; my @SurveyProblems; 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; } } } } if (! scalar(@SurveyProblems)) { $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, part=>$problem->{'part'}, respid=>undef, resptype=>undef}); my $checked = ''; if ($ENV{'form.problemchoice'} eq $value) { $checked = 'checked '; } my $link = $problem->{'res'}->src. '?symb='.&Apache::lonnet::escape($problem->{'res'}->symb); $Str .= ''. ''.$/; } $Str .= '
'.&mt('Survey').'
'. ''.''. '
'; return $Str; } ######################################################### ######################################################### ## ## Compile Student Answers ## ######################################################### ######################################################### sub Compile_Student_Answers { my ($problem,$ProblemData,$Students) = @_; my $resource = $problem->{'resource'}; foreach my $student (@$Students) { 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); 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 =~ /^(option|match)$/) { my @responses = split('&',$student_response); foreach my $response (@responses) { my ($foilid,$option) = map { &Apache::lonnet::unescape($_); } split('=',$response); $data->{'foil_count'}->{$foilid}++; $data->{'foil_responses'}->{$foilid}->{$option}++; } } elsif ($resptype =~ /^(radiobutton)$/) { my ($foil,$value) = map { &Apache::lonnet::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; } } else { # Variable stuff (essays, raw numbers, strings) go here push(@{$data->{'responses'}},$student_response); } } } } return; } ######################################################### ######################################################### ## ## make_Excel_report ## ######################################################### ######################################################### sub make_Excel_report { my ($r,$problem,$problem_data,$students) = @_; &Compile_Student_Answers($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(%{$data->{'foil_responses'}})); 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'}); foreach my $value (sort(keys(%{$data->{'foil_values'}}))) { undef(@rowdata); my $foilid = $data->{'map'}->{$value}; 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; } ######################################################### ######################################################### ## ## make_HTML_report ## ######################################################### ######################################################### 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 @response_ids = $resource->responseIds($partid); my @response_types = $resource->responseType($partid); for (my $i=0;$i<=$#response_ids;$i++) { my $Str = ''.$/; my $respid = $response_ids[$i]; my $resptype = $response_types[$i]; my $data = $problem->{'responsedata'}->{$partid}->{$respid}; if (! defined($data) || ref($data) ne 'HASH') { next; } # Debugging code # $Str .= ''. # ''. # ''. # ''. # ''.$/; $Str .= ''. ''. ''. ''. ''; if (exists($data->{'responses'}) && ref($data->{'responses'}) eq 'ARRAY') { &randomize_array($data->{'responses'}); foreach my $response (@{$data->{'responses'}}) { $response =~ s/\\r\\n/\n/g; $response =~ s/\\'/'/g; $response =~ s/\\"/"/g; $Str .= ''. ''. ''.$/; } } elsif (exists($data->{'foil_count'}) && exists($data->{'foil_responses'})) { $Str.=''; } elsif (exists($data->{'_count'}) && exists($data->{'foil_values'}) && exists($data->{'map'})) { # This is an option or radiobutton survey response my $total = $data->{'_count'}; my $sum = 0; my $tmp; foreach my $value (sort(keys(%{$data->{'foil_values'}}))) { 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'}; $tmp .= ''. ''. ''. ''. ''. ''.$/; } $Str .= ''. ''. ''. ''. ''. ''.$/. $tmp; } $Str.= '
'.$partid.''.$respid.''.$resptype.'
'.&mt('Total').''.$data->{'_count'}.''.&mt('Part [_1], Response [_2]',$partid,$respid).'
'.
                        &HTML::Entities::encode($response,'<>&').
                        '

'. ''; my $tmp = ''; my @foils = sort(keys(%{$data->{'foil_responses'}})); 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 = ''. ''. ''; foreach my $option (sort(@{$prob_data->{'_Options'}})){ my $count = $data->{'foil_responses'}->{$foilid}->{$option}; $tmp .= $preamble. ''. ''. ''.$/; $preamble = ''; } } $Str.=$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').'

'; $r->print($Str); $r->rflush(); } } return; } sub randomize_array { # Fisher Yates shuffle, lifted from p 121 of "The Perl Cookbook" my ($array) = @_; for (my $i=scalar(@$array);--$i;) { my $j = int(rand($i+1)); next if ($i == $j); @$array[$i,$j]=@$array[$j,$i]; } } ######################################################### ######################################################### ## ## Generic Interface Routines ## ######################################################### ######################################################### sub CreateInterface { ## ## Environment variable initialization my $Str = ''; my $output_selector = ''.$/; $Str .= &Apache::lonhtmlcommon::breadcrumbs (undef,'Student Submission Reports'); $Str .= '

'; $Str .= ''."\n"; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''."\n"; # $Str .= ''; # $Str .= ''; # $Str .= ''; # $Str .= ''."\n"; $Str .= '
'.&mt('Sections').''.&mt('Enrollment Status').''.&mt('Output Format').'
'."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= ''.$output_selector.'
'."\n"; # $Str .= ''.&mt('Status: [_1]', ''). ''.'

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