--- modules/gci/londocsgci.pm 2009/12/02 18:35:59 1.4 +++ modules/gci/londocsgci.pm 2009/12/20 04:32:03 1.5 @@ -2,7 +2,7 @@ # Custom Edit Course Routines for Assembly of Valid Concept Tests from # Geoscience Concept Inventory. # -# $Id: londocsgci.pm,v 1.4 2009/12/02 18:35:59 gci Exp $ +# $Id: londocsgci.pm,v 1.5 2009/12/20 04:32:03 gci Exp $ # # Copyright Michigan State University Board of Trustees # @@ -50,20 +50,25 @@ my %probcat; my %prereqs; my @defchosen; my @chosen; +my @mandprobs; +my @bins; +my @optional; my %mandatory; +my @development; +my %developmentitems; sub setdefaults { $path='/res/gci/gci'; $version='GCIv2-1-1'; $reqnum=15; - @allprobs=('01','02','03','04','05','06','07', + @allprobs=('01','02','37','2004_73','03','04','05','06','07', '08','10', '09', '11','12','13','14','15','16','17', '18','69', '19','20', '21','22','23','24','25','26','27','28','29','30', - '31','32','33','34','35','36','37','38', + '31','32','33','34','35','36','38', '39A','39B', '40', '41','42','43','44','45','46','47','48','49','50', @@ -72,26 +77,41 @@ sub setdefaults { '53','54','55','56','58', '60', '61','62','63','64','65','66','67','68','70', - '71', - '2004_73'); + '71'); + @bins = ( ['03','04','06'], + ['07','08','09'], + ['10','12','13','14','15','16','17'], + ['18','19','20','21','22','23','24','25'], + ['26','27','28','30'], + ['32','33','34','35','36'], + ['38','39A','39B','40','41','42','43','44','45','46'], + ['47','48','49','50','51','52','53'], + ['54','55','56','57','58','60','61','62'], + ['63','64','65'], + ['66','67','68','69','70','71']); + @optional = ('05','11','29','31'); @categories=('M1','M2','M3','M4', 'A','B','C','D','E','F','G','H','I','J','K'); - %probcat =('01' => 'M1','02' => 'M2','03' => 'A' ,'04' => 'A' ,'05' => '' ,'06' => 'A' ,'07' => 'B' ,'08' => 'B' ,'09' => 'B' ,'10' => 'C' , - '11' => '' ,'12' => 'C' ,'13' => 'C' ,'14' => 'C' ,'15' => 'C' ,'16' => 'C' ,'17' => 'C' ,'18' => 'D' ,'19' => 'D' ,'20' => 'D' , + %probcat =('01' => 'M1' ,'02' => 'M2' ,'37' => 'M3' ,'2004_73' => 'M4' , + '03' => 'A' ,'04' => 'A','05' => '' ,'06' => 'A' ,'07' => 'B' , + '08' => 'B' ,'09' => 'B' ,'10' => 'C' ,'11' => '' ,'12' => 'C' , + '13' => 'C' ,'14' => 'C' ,'15' => 'C' ,'16' => 'C' ,'17' => 'C' ,'18' => 'D' ,'19' => 'D' ,'20' => 'D' , '21' => 'D' ,'22' => 'D' ,'23' => 'D' ,'24' => 'D' ,'25' => 'D' ,'26' => 'E' ,'27' => 'E' ,'28' => 'E' ,'29' => '' ,'30' => 'E' , - '31' => '' ,'32' => 'F' ,'33' => 'F' ,'34' => 'F' ,'35' => 'F' ,'36' => 'F' ,'37' => 'M3','38' => 'G' , + '31' => '' ,'32' => 'F' ,'33' => 'F' ,'34' => 'F' ,'35' => 'F' ,'36' => 'F' ,'38' => 'G' , '39A'=> 'G' ,'39B'=> 'G' , '40' => 'G' , '41' => 'G' ,'42' => 'G' ,'43' => 'G' ,'44' => 'G' ,'45' => 'G' ,'46' => 'G' ,'47' => 'H' ,'48' => 'H' ,'49' => 'H' ,'50' => 'H' , '51' => 'H' ,'52' => 'H' ,'53' => 'H' ,'54' => 'I' ,'55' => 'I' ,'56' => 'I' ,'57' => 'I' ,'58' => 'I' , '60' => 'I' , '61' => 'I' ,'62' => 'I' ,'63' => 'J' ,'64' => 'J' ,'65' => 'J' ,'66' => 'K' ,'67' => 'K' ,'68' => 'K' ,'69' => 'K' ,'70' => 'K' , - '71' => 'K' , - '2004_73' => 'M4'); - %mandatory=('01' => 1 ,'02' => 1,'37' => 1,'2004_73' => 1); + '71' => 'K'); + %mandatory=('01' => 1 ,'02' => 1,'37' => 1, '2004_73' => 1); + @mandprobs = ('01','02','37','2004_73'); %prereqs=('10' => '08', '57' => '52', '69' => '18'); @defchosen=('01','02','03','07','12','18','26','32','37','38','47','54','63','66','2004_73'); + @development = ('100','101','102'); + %developmentitems = ('100' => 1, '101' => '1', '102' => '1'); } sub checkvalid { @@ -142,56 +162,168 @@ sub validcheck { } sub listresources { - my ($r)=@_; - if ((!&validcheck($r)) || ($env{'form.editmyown'})) { - &editor($r); + my ($r,$context,$cdom,$cnum)=@_; + my $numchosen = scalar(@chosen); + unless ($numchosen > 0) { + $r->print('

'.&mt('Create a Concept Test').'

'. + '

'.&mt('Choose how the test should be built:').'
'. + '

'. + '
'. + '

'. + ''. + '

'); + return; + } + if ((!&validcheck($r)) || ($env{'form.concepttest'} eq 'editmyown')) { + &editor($r,$context,$cdom,$cnum); } else { - $r->print('

'); - $r->print(''); - $r->print('

'); - $r->print(''); - foreach my $item (@defchosen) { - $r->print('print('

'.&mt('This course contains a valid concept test which includes [quant,_1,question].',$numchosen).' '.&mt('Display Contents').'
'); + } + if ($numsub > 0) { + $r->print(&mt('As there are existing student submissions, modification of the [_1]contents[_2] of the Concept Test will result in loss of student performance data, and is [_3]not[_4] permitted.','','','','').'
'.&mt('Modification of open and close dates [_1]is[_2] permitted.','','').'

'); + } else { + $r->print('
'.&mt('Modify Test').''. + &mt('Currently no student submissions have been recorded, so you may modify the contents of the Concept Test.'). + '
'. + ''. + '

'); } - $r->print(''); - $r->print('

'); + my ($crsparms,$ineffect,$parmlev) = ¤t_parms($cdom,$cnum); + my $formname = 'datesform'; + my $datebutton = &mt('Save'); + my $startform = + &Apache::lonhtmlcommon::date_setter($formname,'opendate', + $crsparms->{'opendate'}); + my $endform = + &Apache::lonhtmlcommon::date_setter($formname,'duedate', + $crsparms->{'duedate'}); + $r->print('

'.&mt('Test Availability').''. + &mt('Open and close dates for test items for the concept test are currently set as follows:'). + '
'. + '
'. + &Apache::lonhtmlcommon::start_pick_box(). + &Apache::lonhtmlcommon::row_title(&mt('Open date')). + $startform. + &Apache::lonhtmlcommon::row_closure(). + &Apache::lonhtmlcommon::row_title(&mt('Close date')). + $endform. + &Apache::lonhtmlcommon::end_pick_box().'
'. + ''. + ''. + '

'); } } sub editor { - my ($r)=@_; - my %chosen=(); - foreach my $item (@chosen) { - $chosen{$item}=1; - } - &Apache::lonnet::appenv({'request.gcicontext' => 'buildtest'}); - $r->print('
'); - $r->print('

'.&mt('You may select test items from the list below and then press "Store Problem Selection" at the bottom of the screen.').'

'); - $r->print('

'.&mt('Tests should have at least one item from each bin and [_1] items total.',$reqnum).'

'); - $r->print(&Apache::loncommon::start_data_table(). - &Apache::loncommon::start_data_table_header_row(). - ''.&mt('Select').''.&mt('Problem').''.&mt('Bin').''.&mt('Preview').''. - &Apache::loncommon::end_data_table_header_row()); - foreach my $item (@allprobs) { - $r->print(&Apache::loncommon::start_data_table_row()); - $r->print(''); - if ($mandatory{$item}) { - $r->print('print('print(' checked="checked"'); } - } - $r->print(' />'); - $r->print(''.$item.''.$probcat{$item}.''); - my $output=&Apache::lonindexer::showpreview(&fullurl($item)); - $r->print(' '.($output eq '' ? ' ':$output)." \n"); - - $r->print( &Apache::loncommon::end_data_table_row()); - } - $r->print(&Apache::loncommon::end_data_table()); - $r->print(''); - $r->print('
'); - &Apache::lonnet::delenv('request.gcicontext'); + my ($r,$context,$cdom,$cnum)=@_; + my %chosenitems=(); + my $havedev = 0; + foreach my $item (@chosen) { + $chosenitems{$item}=1; + } + my @devitems = &get_development_questions($cdom,$cnum); + &Apache::lonnet::appenv({'request.gcicontext' => 'buildtest'}); + $r->print('
'); + $r->print(&mt('Select test items from the numbered bins below and then press [_1]"Store Problem Selection"[_2] at the bottom of the page.','','').'
'); + $r->print(''); + my $mandleg = &mt('Mandatory Questions'); + $r->print(&display_questions(\@mandprobs,'mandatory',$mandleg,\%chosenitems)); + for (my $i=0; $i<@bins; $i++) { + my $num = $i+1; + my $legend = &mt('Bin [_1]',$num); + my $catname = 'bin'.$i; + $r->print(&display_questions($bins[$i],$catname,$legend,\%chosenitems)); + } + my $optleg = &mt('Optional Questions'); + $r->print(&display_questions(\@optional,'optional',$optleg,\%chosenitems)); + my $devleg = &mt('Development Questions'); + $r->print(&display_questions(\@devitems,'development',$devleg,\%chosenitems)); + $r->print(''. + ''. + ''. + '
'); + &Apache::lonnet::delenv('request.gcicontext'); +} + +sub display_questions { + my ($questions,$catname,$catlegend,$chosenitems) = @_; + return unless((ref($questions) eq 'ARRAY') && (ref($chosenitems) eq 'HASH')); + my $total = 0; + foreach my $item (@{$questions}) { + if ($chosenitems->{$item}) { + $total ++; + } + } + my $fieldid = 'GCI_'.$catname.'_q'; + my $titleid = 'GCI_'.$catname.'_t'; + my $countid = 'GCI_'.$catname.'_count'; + my $output = '
'; + my %fixed = ( + mandatory => 4, + development => 2, + ); + if (($catname eq 'mandatory') || ($catname eq 'development')) { + $output .= ''.&mt('[_1] [_2] selected automatically',$catlegend, + '').''; + } else { + $output .= ''.&mt('[_1]: currently [_2] selected',$catlegend, + '').''; + } + $output .= ''. + ''. + &mt('Show').' ...
'. + '
'. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(); + unless (($catname eq 'development') || ($catname eq 'mandatory')) { + $output .= ''.&mt('Select').''; + } + $output .= ''.&mt('Problem'). + ''.&mt('Preview').''. + &Apache::loncommon::end_data_table_header_row(); + foreach my $item (@{$questions}) { + my $url = &fullurl($item); + my $title = &Apache::lonnet::metadata($url,'title'); + $output .= &Apache::loncommon::start_data_table_row().''; + if (($catname eq 'mandatory') || ($catname eq 'development')) { + $output .= ''; + } else { + $output .= '{$item}) { $output .= ' checked="checked"'; } + $output .= 'onclick="javascript:countChecked('."'$catname'".');" />'. + ''; + } + $output .= ''.$title.''; + my $content=&Apache::lonindexer::showpreview($url); + $output .= ' '.($content eq '' ? ' ':$content).' '."\n". + &Apache::loncommon::end_data_table_row(); + } + $output .= &Apache::loncommon::end_data_table(). + '

'; + return $output; +} + +sub get_development_questions { + my ($cdom,$cnum) = @_; + my $cid = $cdom.'_'.$cnum; + my %courseenv = &Apache::lonnet::userenvironment($cdom,$cnum, + ('internal.courseowner')); + my $seed = $courseenv{'internal.courseowner'}; + my $rndseed=&Apache::lonnet::rndseed($seed,$cid,$cdom,$cnum); + &Apache::lonnet::setup_random_from_rndseed($rndseed); + my @devitems = &Math::Random::random_permutation(@development); + return ($devitems[0],$devitems[1]); } sub evaluate { @@ -209,14 +341,19 @@ sub mapread_gci { my $coursedom=$env{'course.'.$env{'request.course.id'}.'.domain'}; my $coursenum=$env{'course.'.$env{'request.course.id'}.'.num'}; return - &LONCAPA::map::mapread('/uploaded/'.$coursedom.'/'.$coursenum.'/default.sequence'); + &LONCAPA::map::mapread('/uploaded/'.$coursedom.'/'.$coursenum.'/default_1261144274.sequence'); } sub storemap_gci { - my $coursedom=$env{'course.'.$env{'request.course.id'}.'.domain'}; - my $coursenum=$env{'course.'.$env{'request.course.id'}.'.num'}; + my ($coursedom,$coursenum) = @_; + if (($coursedom !~ /^$match_domain$/) || ($coursenum !~ /^$match_courseid$/)) { + $coursedom=$env{'course.'.$env{'request.course.id'}.'.domain'}; + $coursenum=$env{'course.'.$env{'request.course.id'}.'.num'}; + } + return ('unauthorized',2) if (($coursedom eq '') || ($coursedom eq '') || + (!&Apache::lonnet::allowed('mdc',$coursedom.'_'.$coursenum))); my ($outtext,$errtext)= - &LONCAPA::map::storemap('/uploaded/'.$coursedom.'/'.$coursenum.'/default.sequence',1); + &LONCAPA::map::storemap('/uploaded/'.$coursedom.'/'.$coursenum.'/default_1261144274.sequence',1); if ($errtext) { return ($errtext,2); } return ($errtext,0); } @@ -228,12 +365,14 @@ sub chosen_to_map { } @LONCAPA::map::order=(); @LONCAPA::map::resources=(); + my $counter = 0; for (my $idx=0;$idx<=$#allprobs;$idx++) { my $residx=$idx+1; if ($chosenproblems{$allprobs[$idx]}) { push(@LONCAPA::map::order,$residx); + $counter ++; my $url = &LONCAPA::map::qtunescape(&fullurl($allprobs[$idx])); - my $name = &LONCAPA::map::qtunescape('Problem '.$allprobs[$idx]); + my $name = &LONCAPA::map::qtunescape('Problem '.$counter); $LONCAPA::map::resources[$residx]=join(':', ($name, $url, 'false', 'normal', 'res')); } } @@ -242,32 +381,225 @@ sub chosen_to_map { sub map_to_chosen { @chosen=(); foreach my $idx (@LONCAPA::map::order) { - push(@chosen,$allprobs[$idx-1]); + my ($title,$url)=split(':',$LONCAPA::map::resources[$LONCAPA::map::order[$idx]]); + unless ($url eq '') { + push(@chosen,$allprobs[$idx-1]); + } + } + if (($env{'form.concepttest'} eq 'defchosen') || + ((@chosen == 0) && ($env{'form.concepttest'} eq 'editmyown'))) { + @chosen = @defchosen; } } sub store { - my ($r)=@_; + my ($caller,$cdom,$cnum)=@_; + if ($env{'form.concepttest'} eq 'defchosen') { + @chosen = @defchosen; + } my @errors=&checkvalid(); - if ($#errors>1) { return; } + if (@errors > 0) { + if (($caller eq 'requestcrs') && ($env{'form.concepttest'} eq 'defchosen')) { + return 'Invalid concept test.'; + } else { + return; + } + } &chosen_to_map(); - &storemap_gci(); + my ($err,$errnum) = &storemap_gci($cdom,$cnum); + if ($caller eq 'requestcrs') { + if ($errnum == 0) { + return; + } else { + return 'An error occurred when storing the concept test'; + } + } if ($env{'form.phase'} eq 'storemap') { - unless ($#errors>-1) { - $r->print('

'.&mt('You have successfully assembled a valid test.'). - '

'. - ''. - '

'); + if ($errnum == 0) { + &Apache::lonuserstate::readmap($cdom.'/'.$cnum); + return '

'.&mt('You have successfully assembled a valid test.').'

'. + '

'.&mt('Display Table of Contents'). + ''.(' ' x4).''.&mt('Return to Main Menu').'

'; + } else { + return '
'.&mt('An error occurred when storing your concept test: [_1].',$err).'
'; } } + return; } sub load { &mapread_gci(); &map_to_chosen(); - my @errors=&checkvalid(); - if ($#errors>1) { @chosen=@defchosen; } + if (@chosen > 0) { + my @errors=&checkvalid(); + if ($#errors>1) { @chosen=@defchosen; } + } +} + +sub current_parms { + my ($cdom,$cnum) = @_; + my $courseopt=&Apache::lonnet::get_courseresdata($cnum,$cdom); + my (%crsparms,%ineffect,%parmlev); + foreach my $item ('opendate','duedate') { + $crsparms{$item} = $courseopt->{$env{'request.course.id'}.'.0.'.$item}; + my ($result,@outpar) = + &Apache::lonparmset::parmval('0.'.$item,'0.0',undef,undef, + $cdom,undef,undef,$courseopt); + if ($result) { + $ineffect{$item} = $outpar[$result]; + $parmlev{$item} = $result; + } + } + return (\%crsparms,\%ineffect,\%parmlev); +} + +sub store_dates_parms { + my ($cdom,$cnum) = @_; + my $topsymb = '___0___uploaded/'.$cdom.'/'.$cnum.'/default.sequence'; + my ($opendate,$duedate) = + &Apache::lonuserutils::get_dates_from_form('opendate','duedate'); + my %dates = ( + opendate => { + value => $opendate, + type => 'date_start', + }, + duedate => { + value => $duedate, + type => 'date_end', + }, + ); + my %parmresult; + foreach my $date (keys(%dates)) { + $parmresult{$date} = + &Apache::lonparmset::storeparm_by_symb($topsymb, + '0_'.$date,14,$dates{$date}{'value'}, + $dates{$date}{'type'},undef,$cdom); + } + my $output = &mt('Open and Close dates set for test items').''; + return $output; +} + +sub get_submissions_count { + my ($cdom,$cnum) = @_; + my $navmap = Apache::lonnavmaps::navmap->new(); + if (!defined($navmap)) { + my $itemserror = ''.&mt('An error occurred retrieving information about the course.').'
'.&mt('It is recommended that you [_1]re-select the course[_2].','','').'
'; + return $itemserror; + } + my @allres=$navmap->retrieveResources('/uploaded/'.$cdom.'/'.$cnum.'/default_1261144274.sequence',sub {if ($_[0]->is_problem) { $_[0]->parts();} return 1;}); + my (%resourcetracker,$submissioncount); + my %resourcetracker = &Apache::lonnet::dump('nohist_resourcetracker', + $cdom,$cnum); + foreach my $resource (@allres) { + my $symb = $resource->symb(); + my @parts = @{$resource->parts()}; + foreach my $part (@parts) { + if ($resource->handgrade($part) eq 'yes') { + next; + } + if ($resource->is_survey($part)) { + next; + } + if (exists($resourcetracker{$symb."\0".$part."\0users"})) { + $submissioncount += $resourcetracker{$symb."\0".$part."\0users"}; + } + } + } + return $submissioncount; +} + +sub builder_javascript { + my %lt = &Apache::lonlocal::texthash( + show => 'Show', + hide => 'Hide', + ); + return <$lt{'hide'} ...'; + return; +} + +function hideQuestions(content,title) { + document.getElementById(content).style.display = "none"; + document.getElementById(title).innerHTML='$lt{'show'} ...'; + return; +} + +function setInitialVisibility() { + if (document.getElementById('GCI_mandatory_q') == null) { + return; + } + document.getElementById('GCI_mandatory_q').style.display = "none"; + document.getElementById('GCI_bin0_q').style.display = "none"; + document.getElementById('GCI_bin1_q').style.display = "none"; + document.getElementById('GCI_bin2_q').style.display = "none"; + document.getElementById('GCI_bin3_q').style.display = "none"; + document.getElementById('GCI_bin4_q').style.display = "none"; + document.getElementById('GCI_bin5_q').style.display = "none"; + document.getElementById('GCI_bin6_q').style.display = "none"; + document.getElementById('GCI_bin7_q').style.display = "none"; + document.getElementById('GCI_bin8_q').style.display = "none"; + document.getElementById('GCI_bin9_q').style.display = "none"; + document.getElementById('GCI_bin10_q').style.display = "none"; + document.getElementById('GCI_optional_q').style.display = "none"; + document.getElementById('GCI_development_q').style.display = "none"; +} + +function countChecked(binname) { + var count = 0; + for (var i=0; i