# The LearningOnline Network with CAPA # # $Id: lonstatistics.pm,v 1.156.2.7 2016/08/14 16:38:05 raeburn 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/ # # (Navigate problems for statistical reports # ### package Apache::lonstatistics; use strict; use Apache::Constants qw(:common :http); use vars qw( @FullClasslist @Students @Sections @Groups %StudentData @StudentDataOrder @SelectedStudentData $enrollment_status); use Apache::lonnet; use Apache::lonhomework; use Apache::loncommon; use Apache::loncoursedata; use Apache::lonhtmlcommon; use Apache::lonmysql; use Apache::lonlocal; use Apache::longroup; use Time::HiRes; # # Statistics Packages use Apache::lonproblemanalysis(); use Apache::lonsubmissiontimeanalysis(); use Apache::loncorrectproblemplot(); use Apache::lonproblemstatistics(); use Apache::lonstudentassessment(); use Apache::lonpercentage; use Apache::lonstudentsubmissions(); use Apache::lonsurveyreports(); use Apache::longradinganalysis(); use Apache::lonquickgrades(); use LONCAPA; # # Classlist variables # my $curr_student; my $prev_student; my $next_student; sub clear_classlist_variables { undef(@FullClasslist); undef(@Students); undef(@Sections); undef(@Groups); undef(%StudentData); undef(@SelectedStudentData); undef($curr_student); undef($prev_student); undef($next_student); } sub PrepareClasslist { my %Sections; &clear_classlist_variables(); # # Retrieve the classlist my $cid = $env{'request.course.id'}; my $cdom = $env{'course.'.$cid.'.domain'}; my $cnum = $env{'course.'.$cid.'.num'}; my ($classlist,$field_names) = &Apache::loncoursedata::get_classlist($cdom, $cnum); my @selected_sections = &get_selected_sections(); my @selected_groups = &get_selected_groups(); # # Deal with instructors with restricted section access if ($env{'request.course.sec'} !~ /^\s*$/) { @selected_sections = ($env{'request.course.sec'}); } # # Set up %StudentData @StudentDataOrder = qw/fullname username domain id section status groups comments/; foreach my $field (@StudentDataOrder) { $StudentData{$field}->{'title'} = &mt($field); $StudentData{$field}->{'base_width'} = length(&mt($field)); $StudentData{$field}->{'width'} = $StudentData{$field}->{'base_width'}; } # # get the status requested $enrollment_status = 'Active'; $enrollment_status = $env{'form.Status'} if (exists($env{'form.Status'})); # # Get groupmembership my ($classgroups,$studentgroups); my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum); if (%curr_groups) { ($classgroups,$studentgroups) = &Apache::loncoursedata::get_group_memberships($classlist, $field_names, $cdom,$cnum); } my $now = time; # Process the classlist while (my ($student,$student_data) = each (%$classlist)) { my $studenthash = (); for (my $i=0; $i< scalar(@$field_names);$i++) { my $field = $field_names->[$i]; # Store the data $studenthash->{$field}=$student_data->[$i]; # Keep track of the width of the fields next if (! exists($StudentData{$field})); my $length = length($student_data->[$i]); if ($StudentData{$field}->{'width'} < $length) { $StudentData{$field}->{'width'} = $length; } } my @studentsgroups = &Apache::loncoursedata::get_students_groups ($student,$enrollment_status, $classgroups); if (@studentsgroups) { $studenthash->{'groups'} = join(', ',@studentsgroups); $studenthash->{'groupref'} = \@studentsgroups; } else { $studenthash->{'groups'} = 'none'; $studenthash->{'groupref'} = []; } push (@FullClasslist,$studenthash); # # Build up a list of sections my $section = $studenthash->{'section'}; if (! defined($section) || $section =~/^\s*$/ || $section == -1) { $studenthash->{'section'} = 'none'; $section = $studenthash->{'section'}; } $Sections{$section}++; # # Only put in the list those students we are interested in foreach my $sect (@selected_sections) { if ( (($sect eq 'all') || ($section eq $sect)) && (($studenthash->{'status'} eq $enrollment_status) || ($enrollment_status eq 'Any')) ){ my $groupcheck = 0; if (grep(/^all$/,@selected_groups)) { push(@Students,$studenthash); last; } elsif (grep(/^none$/,@selected_groups)) { if ($studenthash->{'groups'} eq 'none') { push(@Students,$studenthash); last; } } else { foreach my $group (@selected_groups) { if (grep(/^$group$/,@studentsgroups)) { push(@Students,$studenthash); $groupcheck = 1; last; } } if ($groupcheck) { last; } } } } } # # Put the consolidated section data in the right place if ($env{'request.course.sec'} !~ /^\s*$/) { @Sections = ($env{'request.course.sec'}); } else { @Sections = sort { if ($a == $a && $b == $b ) { return $a <=> $b; } return $a cmp $b; } keys(%Sections); unshift(@Sections,'all'); # Put 'all' at the front of the list } # Sort the groups @Groups = sort {$a cmp $b} keys(%{$studentgroups}); unshift(@Groups,'all'); # Put 'all' at the front of the list # # Sort the Students my $sortby = 'fullname'; $sortby = $env{'form.sort'} if (exists($env{'form.sort'})); my @TmpStudents = sort { lc($a->{$sortby}) cmp lc($b->{$sortby}) || lc($a->{'fullname'}) cmp lc($b->{'fullname'}) || lc($a->{'username'}) cmp lc($b->{'username'}) } @Students; @Students = @TmpStudents; # # Now deal with that current student thing.... $curr_student = undef; if (exists($env{'form.SelectedStudent'})) { my ($current_uname,$current_dom) = split(':',$env{'form.SelectedStudent'}); my $i; for ($i = 0; $i<=$#Students; $i++) { next if (($Students[$i]->{'username'} ne $current_uname) || ($Students[$i]->{'domain'} ne $current_dom)); $curr_student = $Students[$i]; last; # If we get here, we have our student. } if (defined($curr_student)) { if ($i == 0) { $prev_student = undef; } else { $prev_student = $Students[$i-1]; } if ($i == $#Students) { $next_student = undef; } else { $next_student = $Students[$i+1]; } } } # if (exists($env{'form.StudentData'})) { @SelectedStudentData = &Apache::loncommon::get_env_multiple('form.StudentData'); } else { @SelectedStudentData = ('username'); } foreach (@SelectedStudentData) { if ($_ eq 'all') { @SelectedStudentData = ('all'); last; } } # return; } sub get_selected_sections { my @selected_sections = &Apache::loncommon::get_env_multiple('form.Section'); @selected_sections = ('all') if (! @selected_sections); foreach (@selected_sections) { if ($_ eq 'all') { @selected_sections = ('all'); } } # # Deal with instructors with restricted section access if ($env{'request.course.sec'} !~ /^\s*$/) { @selected_sections = ($env{'request.course.sec'}); } return @selected_sections; } sub get_selected_groups { my @selected_groups = &Apache::loncommon::get_env_multiple('form.Group'); @selected_groups = ('all') if (! @selected_groups); foreach my $grp (@selected_groups) { if ($grp eq 'all') { @selected_groups = ('all'); last; } } return @selected_groups; } sub section_and_enrollment_description { my ($mode) = @_; if (! defined($mode)) { $mode = 'localized'; } my @sections = &Apache::lonstatistics::get_selected_sections(); my @groups = &Apache::lonstatistics::get_selected_groups(); my $description; if ($mode eq 'localized') { $description = &mt('Unable to determine section, groups and access status'); } elsif ($mode eq 'plaintext') { $description = 'Unable to determine section, groups and access status'; } else { $description = 'Bad parameter passed to lonstatistics::section_and_enrollment_description'; &Apache::lonnet::logthis($description); } $description = §ion_or_group_text($mode,'section',@sections). ' '.§ion_or_group_text($mode,'group',@groups); if ($mode eq 'localized') { $description .= ' '.&mt($env{'form.Status'}.' access status.'); } elsif ($mode eq 'plaintext') { $description .= ' '.$env{'form.Status'}.' access status.'; } return $description; } sub section_or_group_text { my ($mode,$type,@items) = @_; my $text; my %phrases = (); %{$phrases{'section'}} = ( single => 'Section', all => 'All sections', plural => 'Sections', ); %{$phrases{'group'}} = ( single => 'Group', all => 'All groups', plural => 'Groups', ); if (scalar(@items) == 1 && $items[0] ne 'all') { if ($mode eq 'localized') { $text = &mt($phrases{$type}{single}.' [_1].',$items[0]); } elsif ($mode eq 'plaintext') { $text = $phrases{$type}{single}.' '.$items[0].'.'; } } elsif (scalar(@items) && $items[0] eq 'all') { if ($mode eq 'localized') { $text = &mt($phrases{$type}{all}.'.'); } elsif ($mode eq 'plaintext') { $text = $phrases{$type}{all}.'.'; } } elsif (scalar(@items)) { my $lastitem = pop(@items); if ($mode eq 'localized') { $text = &mt($phrases{$type}{plural}.' [_1] and [_2].', join(', ',@items),$lastitem); } elsif ($mode eq 'plaintext') { $text = $phrases{$type}{plural}.' '.join(', ',@items).' and '. $lastitem.'.'; } } return $text; } sub get_students { if (! @Students) { &PrepareClasslist(); } return @Students; } sub current_student { return $curr_student; } sub previous_student { return $prev_student; } sub next_student { return $next_student; } sub StudentDataSelect { my ($elementname,$status,$numvisible)=@_; if ($numvisible < 1) { return; } # # Build the form element my $Str = "\n"; $Str .= '\n"; return $Str; } sub get_selected_maps { my ($elementname) = @_; my @selected_maps = &Apache::loncommon::get_env_multiple('form.'.$elementname); @selected_maps = ('all') if (! @selected_maps); foreach my $map (@selected_maps) { if ($map eq 'all') { @selected_maps = ('all'); last; } } return @selected_maps; } sub selected_sequences_with_assessments { my ($mode) = @_; $mode = 'selected' if (! defined($mode)); my $navmap = Apache::lonnavmaps::navmap->new(); if (!defined($navmap)) { return ('Can not open Coursemap'); } # my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },1,0,1); my $toplevelseq = $navmap->getById('0.0'); if (!grep(/^\Q$toplevelseq\E$/,@sequences)) { unshift(@sequences,$toplevelseq); } my @sequences_with_assessments; foreach my $sequence (@sequences) { if ($navmap->hasResource($sequence,sub { shift->is_problem(); },0,1)){ push(@sequences_with_assessments,$sequence); } } # my @sequences_to_show; foreach my $sequence (@sequences_with_assessments) { if ($mode eq 'all') { push (@sequences_to_show,$sequence); } elsif ($mode eq 'selected') { foreach my $map_symb (&get_selected_maps('Maps')) { if ($sequence->symb eq $map_symb || $map_symb eq 'all'){ push (@sequences_to_show,$sequence); last; # Only put it in once } } } } return $navmap,@sequences_to_show; } sub map_select { my ($elementname,$status,$numvisible)=@_; if ($numvisible < 1) { return; } # # Set up array of selected items my @selected_maps = &get_selected_maps($elementname); # # Build the form element my $form = "\n"; $form .= '\n"; return $form; } sub SectionSelect { my ($elementname,$status,$numvisible)=@_; if ($numvisible < 1) { return; } # # Make sure we have the data we need to continue if (! @Sections) { &PrepareClasslist() } # # Make sure course's student table is up to date if (@Sections) { &Apache::loncoursedata::ensure_current_sections(); } # # Build the form element my $Str = "\n"; $Str .= '\n"; return $Str; } sub GroupSelect { my ($elementname,$status,$numvisible)=@_; if ($numvisible < 1) { return; } # # Make sure we have the data we need to continue if (! @Groups) { &PrepareClasslist(); } # # Make sure course's groupnames and studentgroups tables # are up to date. # if (@Groups) { &Apache::loncoursedata::ensure_current_groups(); &Apache::loncoursedata::ensure_current_students_groups(); } # # Build the form element my $Str = "\n"; $Str .= '\n"; } sub DisplayClasslist { my ($r)=@_; &Apache::lonhtmlcommon::add_breadcrumb ({text=>'Select One Student'}); # # Output some of the standard interface components my $Str; $Str .= &Apache::lonhtmlcommon::breadcrumbs('Select One Student'); $Str .= ''."\n"; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''.$/; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''.$/; $Str .= '
'.&mt('Sections').''.&mt('Groups').''.&mt('Access Status').'
'. &Apache::lonstatistics::SectionSelect('Section','multiple',5). ''. &Apache::lonstatistics::GroupSelect('Group','multiple',5). ''. &Apache::lonhtmlcommon::StatusOptions(undef,undef,5). '
'; $Str .= ''; $r->print($Str); $r->rflush(); # my @Fields = ('fullname','username','domain','id','section','status','groups'); # $Str = ''; my @selected_sections = &get_selected_sections(); if (! @Students) { if ($selected_sections[0] eq 'all') { if (lc($env{'form.Status'}) eq 'active') { $Str .= '

'. &mt('There are no currently enrolled students in the course.'). '

'; } elsif (lc($env{'form.Status'}) eq 'expired') { $Str .= '

'. &mt('There are no previously enrolled students in the course.'). '

'; } elsif (lc($env{'form.Status'}) eq 'future') { $Str .= '

'. &mt('There are no students with future access in the course.'). '

'; } else { # 'any' and any others $Str .= '

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

'; } } else { if (lc($env{'form.Status'}) eq 'active') { $Str .= '

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

'; } elsif (lc($env{'form.Status'}) eq 'expired') { $Str .= '

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

'; } elsif (lc($env{'form.Status'}) eq 'future') { $Str .= '

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

'; } else { # 'any' and any others $Str .= '

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

'; } } $Str.= '

' .'' .&mt('Return to the chart').'' .'

'; $r->print($Str); $r->rflush(); return; } $Str .= '

'.&mt('Select One Student').'

' .'

'.&mt("Click on a student's name or username to view their chart.").'

' .&Apache::loncommon::start_data_table() .&Apache::loncommon::start_data_table_header_row(); foreach my $field (@Fields) { $Str .= ''.&mt($field). ''; } $Str .= &Apache::loncommon::end_data_table_header_row(); # foreach my $student (@Students) { # @Students is a package variable my $sname = $student->{'username'}.':'.$student->{'domain'}; $Str .= &Apache::loncommon::start_data_table_row(); # foreach my $field (@Fields) { $Str .= ''; if ($field eq 'fullname' || $field eq 'username') { $Str .= ''; $Str .= $student->{$field}; $Str .= ''; } elsif ($field eq 'status') { $Str .= &mt($student->{$field}); } else { if ($student->{$field} eq 'none') { $Str .= &mt('none') } else { $Str .= $student->{$field}; } } $Str .= ''; } $Str .= &Apache::loncommon::end_data_table_row(); } $Str .= &Apache::loncommon::end_data_table(); # $r->print($Str); $r->rflush(); # return; } sub CreateMainMenu { # # Define menu data my @reports = ( {categorytitle => 'Statistics and Analyses', items => [ {url => '/adm/statistics?reportSelected=problem_statistics', permission => 'F', icon => 'document-open.png', linktext => ('Overall Problem Statistics'), linktitle => ('Student performance statistics on all problems.')}, {url => '/adm/statistics?reportSelected=problem_analysis', permission => 'F', icon => 'prob_ana.png', linktext => ('Detailed Problem Analysis'), linktitle => ('Detailed statistics and graphs of student performance on problems.')}, ]}, {categorytitle => 'Plots', items => [ {url => '/adm/statistics?reportSelected=submissiontime_analysis', permission => 'F', icon => 'subtimpl.png', linktext => ('Submission Time Plots'), linktitle => ('Display and analysis of submission times on assessments.')}, {url => '/adm/statistics?reportSelected=correct_problems_plot', permission => 'F', icon => 'coprplot.png', linktext => ('Correct Problems Plot'), linktitle => ('Display a histogram of student performance in the course.')}, ]}, {categorytitle => 'Reports', items => [ {url => '/adm/statistics?reportSelected=student_submission_reports', permission => 'F', icon => 'edit-copy.png', linktext => ('Student Submission Reports'), linktitle => ('Prepare reports of student submissions.')}, {url => '/adm/statistics?reportSelected=survey_reports', permission => 'F', icon => 'survey_rep.png', linktext => ('Survey Reports'), linktitle => ('Prepare reports on survey results.')}, ]}); return &Apache::lonhtmlcommon::generate_menu(@reports); } sub handler { my $r=shift; my $c = $r->connection(); # # Check for access if (! &Apache::lonnet::allowed('vgr',$env{'request.course.id'})) { $env{'user.error.msg'}= $r->uri.":vgr:0:0:Cannot view grades for complete course"; if (! &Apache::lonnet::allowed('vgr', $env{'request.course.id'}.'/'.$env{'request.course.sec'})) { $env{'user.error.msg'}= $r->uri.":vgr:0:0:Cannot view grades with given role"; return HTTP_NOT_ACCEPTABLE; } } # # Send the header &Apache::loncommon::no_cache($r); &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; if ($r->header_only) { return OK; } # # Extract form elements from query string &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['sort','reportSelected', 'SelectedStudent']); # # Give the LON-CAPA page header my $style = < ul.sub_studentans { list-style-type: none } ul.sub_correctans { list-style-type: none } tr.even { background-color: \#CCCCCC } td.essay { border: 1px solid gray; } ENDSTYLE $r->print(&Apache::loncommon::start_page('Course Statistics and Charts', $style)); $r->rflush(); # # Either print out a menu for them or send them to a report &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/statistics', title=>'Statistics', text =>'Statistics', faq=>139, bug=>'Statistics and Charts'}); if (! exists($env{'form.reportSelected'}) || $env{'form.reportSelected'} eq '') { $r->print(&Apache::lonhtmlcommon::breadcrumbs('Statistics Main Page'). &CreateMainMenu()); } else { # if (! &Apache::lonmysql::verify_sql_connection()) { my $serveradmin = $r->dir_config('lonAdmEMail'); $r->print('

'. &mt('Unable to connect to database!'). '

'); $r->print('

' .&mt('Please notify the server administrator [_1]', ,''.$serveradmin.'') .'

'); $r->print('

'. &mt('Course Statistics and Charts cannot be '. 'retrieved until the database is restarted. '. 'Your data is intact but cannot be displayed '. 'at this time.').'

'); $r->print(&Apache::loncommon::end_page()); return; } # # Clean out the caches if (exists($env{'form.ClearCache'})) { &Apache::loncoursedata::delete_caches($env{'request.course.id'}); } # my $GoToPage = $env{'form.reportSelected'}; # # Begin form output $r->print('
print('onsubmit="return checkanon();" '); } $r->print('method="post" action="/adm/statistics">'); $r->rflush(); $r->print(''); if($GoToPage eq 'activitylog') { # &Apache::lonproblemstatistics::Activity(); } elsif($GoToPage eq 'problem_statistics') { &Apache::lonhtmlcommon::add_breadcrumb ({href=>'/adm/statistics?reportselected=problem_statistics', text=>'Overall Problem Statistics'}); &Apache::lonproblemstatistics::BuildProblemStatisticsPage($r,$c); } elsif($GoToPage eq 'problem_analysis') { &Apache::lonhtmlcommon::add_breadcrumb ({href=>'/adm/statistics?reportselected=problem_analysis', text=>'Detailed Problem Analysis'}); &Apache::lonproblemanalysis::BuildProblemAnalysisPage($r,$c); } elsif($GoToPage eq 'submissiontime_analysis') { &Apache::lonhtmlcommon::add_breadcrumb ({href=> '/adm/statistics?reportselected=submissiontime_analysis', text=>'Submission Time Plots'}); &Apache::lonsubmissiontimeanalysis::BuildSubmissionTimePage($r,$c); } elsif($GoToPage eq 'student_submission_reports') { &Apache::lonhtmlcommon::add_breadcrumb ({href=> '/adm/statistics?reportselected=student_submission_reports', text=>'Student Submission Reports'}); &Apache::lonstudentsubmissions::BuildStudentSubmissionsPage($r,$c); } elsif($GoToPage eq 'survey_reports') { &Apache::lonhtmlcommon::add_breadcrumb ({href=> '/adm/statistics?reportselected=survey_reports', text=>'Survey Reports'}); &Apache::lonsurveyreports::BuildSurveyReportsPage($r,$c); } elsif($GoToPage eq 'correct_problems_plot') { &Apache::lonhtmlcommon::add_breadcrumb ({href=>'/adm/statistics?reportselected=correct_problems_plot', text=>'Correct Problems Plot'}); &Apache::loncorrectproblemplot::BuildCorrectProblemsPage($r,$c); } elsif($GoToPage eq 'student_assessment') { &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb ({href=>'/adm/statistics?reportselected=student_assessment', text=>'Chart'}); &Apache::lonstudentassessment::BuildStudentAssessmentPage($r,$c); } elsif($GoToPage eq 'grading_analysis') { &Apache::lonhtmlcommon::add_breadcrumb ({href=>'/adm/statistics?reportselected=grading_anaylsis', text=>'Grading Analysis'}); &Apache::longradinganalysis::build_grading_analysis_page($r,$c); } # $r->print("
\n"); } $r->print(&Apache::loncommon::end_page()); $r->rflush(); # return OK; } 1; __END__ =pod =head1 NAME lonstatistics =head1 SYNOPSIS Main handler for statistics and chart. This is part of the LearningOnline Network with CAPA project described at http://www.lon-capa.org. =head1 PACKAGE VARIABLES =over =item @FullClasslist The full classlist =item @Students The students we are concerned with for this invocation =item @Sections The sections available in this class =item @Groups The groups available in the class =item $curr_student The student currently being examined =item $prev_student The student previous in the classlist =item $next_student The student next in the classlist =back =head1 SUBROUTINES =over =item &clear_classlist_variables() undef the following package variables: =over 4 =item * @FullClasslist =item * @Students =item * @Sections =item * @Groups =item * %StudentData =item * @StudentDataOrder =item * @SelectedStudentData =item * $curr_student =item * $prev_student =item * $next_student =back =item &PrepareClasslist() Build up the classlist information. The classlist information is kept in the following package variables: =over 4 =item * @FullClasslist =item * @Students =item * @Sections =item * @Groups =item * %StudentData =item * @SelectedStudentData =item * $curr_student =item * $prev_student =item * $next_student =back $curr_student, $prev_student, and $next_student may not be defined, depending upon the calling context. =item get_selected_sections() Returns an array of the selected sections =item get_selected_groups() Returns an array of the selected groups =item §ion_and_enrollment_description() Returns a string describing the currently selected section(s), group(s) and access status. Inputs: mode = 'plaintext' or 'localized' (defaults to 'localized') 'plaintext' is used for example in Excel spreadsheets. Returns: scalar description string. =item section_or_group_text() =item get_students() Returns a list of the selected students =item ¤t_student() Returns a pointer to a hash containing data about the currently selected student. =item &previous_student() Returns a pointer to a hash containing data about the student prior in the list of students. Or something. =item &next_student() Returns a pointer to a hash containing data about the next student to be viewed. =item &StudentDataSelect($elementname,$status,$numvisible,$selected) Returns html for a selection box allowing the user to choose one (or more) of the fields of student data available (fullname, username, id, section, etc) =over 4 =item * $elementname The name of the HTML form element =item * $status 'multiple' or 'single' selection box =item * $numvisible The number of options to be visible =back =item &get_selected_maps($elementname) Input: Name of the