# Handler for parsing text upload problem descriptions into .problems
# $Id: testbankimport.pm,v 1.46 2023/07/23 01:09:04 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/
#
package Apache::testbankimport;
use strict;
use Apache::Constants qw(:common :http :methods);
use Apache::loncommon();
use Apache::lonnet;
use HTML::Entities();
use Apache::lonlocal;
use Apache::lonupload;
use Apache::londocs;
use File::Basename();
use LONCAPA();
use File::MMagic;
use XML::DOM;
use RTF::HTMLConverter;
use HTML::TokeParser;
# ---------------------------------------------------------------- Display Control
sub display_control {
# figure out what page we're on and where we're heading.
my $page = $env{'form.page'};
my $command = $env{'form.go'};
my $current_page = &calculate_page($page,$command);
return $current_page;
}
# CALCULATE THE CURRENT PAGE
sub calculate_page($$) {
my ($prev,$dir) = @_;
return 0 if $prev eq ''; # start with first page
return $prev + 1 if $dir eq 'NextPage';
return $prev - 1 if $dir eq 'PreviousPage';
return $prev if $dir eq 'ExitPage';
return 0 if $dir eq 'BackToStart';
}
sub jscript_zero {
my ($webpath,$jsref) = @_;
my $start_page =
&Apache::loncommon::start_page('Create Testbank directory',undef,
{'only_body' => 1,
'js_ready' => 1,});
my $end_page =
&Apache::loncommon::end_page({'js_ready' => 1,});
my %lt = &Apache::lonlocal::texthash(
loca => 'Location',
newd => 'New Directory',
ente => 'Enter the name of the new directory where you will save the converted testbank questions',
go => 'Go',
);
$$jsref = <<"END_SCRIPT";
function createWin() {
document.info.newdir.value = "";
newWindow = window.open("","CreateDir","HEIGHT=400,WIDTH=750,scrollbars=yes")
newWindow.document.open()
newWindow.document.write('$start_page')
newWindow.document.write("\\n")
newWindow.document.write("
$lt{'loca'}: $webpath
$lt{'newd'}
\\n")
newWindow.document.write("")
newWindow.document.write('$end_page')
newWindow.document.close()
newWindow.focus()
}
END_SCRIPT
return;
}
# ---------------------------------------------------------------- Jscript One
sub jscript_one {
my $jsref = shift;
$$jsref = <<"END_SCRIPT";
function verify() {
if ((document.forms.display.blocks.value == "") || (!document.forms.display.blocks.value) || (document.forms.display.blocks.value == "0")) {
alert("You must enter the number of blocks of questions of a given question type. This number must be 1 or more.")
return false
}
if (document.forms.display.qnumformat.options[document.forms.display.qnumformat.selectedIndex].value == "-1") {
alert("You must select the format used for the question number, e.g., (1), 1., (1, or 1).")
return false
}
return true
}
function nextPage() {
if (verify()) {
document.forms.display.go.value="NextPage"
document.forms.display.submit()
}
}
function backPage() {
document.forms.display.go.value="PreviousPage"
document.forms.display.submit()
}
function setElements() {
var iter = 0
var selParam = 0
END_SCRIPT
if (exists($env{'form.blocks'}) ) {
$$jsref .= qq|
document.forms.display.blocks.value = $env{'form.blocks'}\n|;
}
if (exists($env{'form.qnumformat'}) ) {
$$jsref .= <<"TO_HERE";
for (iter=0; iter= curmin ) {
alert("The question number range for block "+iter+" overlaps with the question number range for one of the previous blocks - this is not permitted.")
return false
}
}
else {
if (parseInt(poolForm.elements[5*i+1].value) <= curmax) {
for (var j=parseInt(poolForm.elements[5*i+1].value); j<=parseInt(poolForm.elements[5*i+2].value); j++) {
for (var k=0; k= parseInt(poolForm.elements[5*k+1].value)) && (j <= parseInt(poolForm.elements[5*k+2].value))) {
var overlap = k+1
alert("The question number range for block "+iter+" overlaps with the question number range for block "+overlap+" - this is not permitted.")
return false
}
}
}
}
}
if (parseInt(poolForm.elements[5*i+1].value) < curmin) {
curmin = parseInt(poolForm.elements[5*i+1].value)
}
if (parseInt(poolForm.elements[5*i+2].value) > curmax) {
curmax = parseInt(poolForm.elements[5*i+2].value)
}
}
}
if (curmax >$qcount+curmin) {
alert("The last # for one or more of the blocks is too large - the last number of the last block can not be greater than $qcount: the total number of questions in the uploaded file.")
return false
}
var endpt = $qcount + curmin
for (var n=curmin; n= parseInt(poolForm.elements[5*m+1].value)) && (n <= parseInt(poolForm.elements[5*m+2].value))) {
warnFlag = false
}
}
if (warnFlag) {
alert("The question type for question "+n+" could not be identified because it does not fall within the number ranges you have provided for any of the $blocks block(s)")
return false
}
}
return true
}
function nextPage() {
if (verify()) {
document.forms.display.go.value="NextPage"
document.forms.display.submit()
}
}
function backPage() {
document.forms.display.go.value="PreviousPage"
document.forms.display.submit()
}
function colSet(caller) {
var poolForm = document.forms.display
var curVal = poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value
poolForm.elements[caller*5+4].length = 0
if (poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "-1") {
poolForm.elements[caller*5+4].options[0] = new Option("<--- Set type ","-1",true,true)
}
else {
if ((poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "MC") || (poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "MA") || (poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "Ord")) {
poolForm.elements[caller*5+4].options[0] = new Option("Select","-1",true,true)
poolForm.elements[caller*5+4].options[1] = new Option("a ","lcspace",false,false)
poolForm.elements[caller*5+4].options[2] = new Option("A ","ucspace",false,false)
poolForm.elements[caller*5+4].options[3] = new Option("a.","lcperiod",false,false)
poolForm.elements[caller*5+4].options[4] = new Option("A.","ucperiod",false,false)
poolForm.elements[caller*5+4].options[5] = new Option("(a)","lcparen",false,false)
poolForm.elements[caller*5+4].options[6] = new Option("(A)","ucparen",false,false)
poolForm.elements[caller*5+4].options[7] = new Option("a)","lconeparen",false,false)
poolForm.elements[caller*5+4].options[8] = new Option("A)","uconeparen",false,false)
poolForm.elements[caller*5+4].options[9] = new Option("a.)","lcdotparen",false,false)
poolForm.elements[caller*5+4].options[10] = new Option("A.)","ucdotparen",false,false)
poolForm.elements[caller*5+4].options[11] = new Option("(i)","romparen",false,false)
poolForm.elements[caller*5+4].options[12] = new Option("i)","romoneparen",false,false)
poolForm.elements[caller*5+4].options[13] = new Option("i.)","romdotparen",false,false)
poolForm.elements[caller*5+4].options[14] = new Option("i.","romperiod",false,false)
poolForm.elements[caller*5+4].selectedIndex = 0
}
else {
poolForm.elements[caller*5+4].options[0] = new Option("Not required","0",true,true)
}
}
poolForm.elements[caller*5+5].length = 0
if (poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "-1") {
poolForm.elements[caller*5+5].options[0] = new Option("<--- Set type ","-1",true,true)
}
else {
if ((poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "MA") || (poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "FIB")) {
poolForm.elements[caller*5+5].options[0] = new Option("Select","-1",true,true)
poolForm.elements[caller*5+5].options[1] = new Option("single answer","single",false,false)
poolForm.elements[caller*5+5].options[2] = new Option("comma","comma",false,false)
poolForm.elements[caller*5+5].options[3] = new Option("space","space",false,false)
poolForm.elements[caller*5+5].options[4] = new Option("new line","line",false,false)
poolForm.elements[caller*5+5].options[5] = new Option("tab","tab",false,false)
}
else {
if (poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "Ord") {
poolForm.elements[caller*5+5].options[0] = new Option("Select","-1",true,true)
poolForm.elements[caller*5+5].options[1] = new Option("comma","comma",false,false)
poolForm.elements[caller*5+5].options[2] = new Option("space","space",false,false)
poolForm.elements[caller*5+5].options[3] = new Option("new line","line",false,false)
poolForm.elements[caller*5+5].options[4] = new Option("tab","tab",false,false)
}
else {
if (poolForm.elements[caller*5+3].options[poolForm.elements[caller*5+3].selectedIndex].value == "TF") {
poolForm.elements[caller*5+5].options[0] = new Option("Select","-1",true,true)
poolForm.elements[caller*5+5].options[1] = new Option("True or False","word",false,false)
poolForm.elements[caller*5+5].options[2] = new Option("true or false","word",false,false)
poolForm.elements[caller*5+5].options[3] = new Option("TRUE or FALSE","word",false,false)
poolForm.elements[caller*5+5].options[4] = new Option("T or F","lett",false,false)
poolForm.elements[caller*5+5].options[5] = new Option("t or f","lett",false,false)
}
else {
poolForm.elements[caller*5+5].options[0] = new Option("Not required","0",true,true)
}
}
}
}
}
function setElements() {
var iter = 0
var selParam = 0
END_SCRIPT
my @names = ("start_","end_","qtype_","foilformat_","ansr_");
for (my $x=0; $x<$blocks; $x++) {
foreach my $name (@names) {
my $parname = $name.$x;
my $value = $env{"form.$parname"};
if ($value ne "") {
if (($name eq "start_") || ($name eq "end_")) {
$$jsref .= qq|
document.forms.display.$parname.value = $value\n|;
} elsif ($name eq "qtype_") {
$$jsref .= qq|
for (iter=0; iter 0) {
my $maxnum = $numitems - 1;
my %lt = &Apache::lonlocal::texthash(
fnmb => 'Filenames must be unique',
isum => 'is used more than once',
);
$$jsref .= qq|
for (var j=$maxnum; j>0; j--) {
var currname = document.dataForm.elements['probfile_'+j].value;
for (var k=j-1; k>=0; k--) {
var comparename = document.dataForm.elements['probfile_'+k].value;
if (currname == comparename) {
alert("$lt{fnmb} - "+currname+" $lt{isum}");
return false;
}
}
}
|;
}
$$jsref .= '
return true;
}
';
$$jsref .= &Apache::loncommon::check_uncheck_jscript();
return;
}
# ---------------------------------------------------------------- Jscript Four
sub jscript_four {
my ($jsref,$webpath) = @_;
$$jsref = qq|
function backtoStart() {
document.location.href="$webpath"
}
function backPage() {
document.forms.verify.go.value="PreviousPage"
document.forms.verify.submit();
}
|;
}
# ---------------------------------------------------------------- Display Zero
sub display_zero {
my ($r,$fn,$page,$webpath) = @_;
my $go_default = 'NextPage';
if ($fn eq '') {
$r->print(''.&mt('Incomplete file upload').' '.&mt('Return to the [_1]Authoring Space menu[_2] to upload a file','',''));
}
$r->print(&mt('The [_1]Testbank Upload[_2] utility can be used by LON-CAPA authors to generate LON-CAPA problem files from a testbank file of questions/answers.','','').' '.
&mt('The following question types can be converted:').'
'.&mt('multiple choice').'
'.&mt('multiple answer correct').'
'.&mt('fill-in-the-blank').'
'.&mt('ordering/ranking').'
'.&mt('true/false').'
'.&mt('essay').'
'.&mt('The file of questions (in plain text, RTF or HTML format) must meet certain requirements for the conversion process to generate functioning LON-CAPA problems.').&Apache::loncommon::help_open_topic('Testbank_Formatting').' '.
&mt('Five steps are involved in the conversion process.').'
'.&mt('Optionally create a new sub-directory where the converted testbank questions will be saved.').'
'.&mt('Provide information about the question format - i.e., question numbering style, and the number of blocks of questions of each question type.').'
'.&mt('Provide information about the questions in each block, including question type, start and end question numbers for each block, and foil labelling style and answer format where required.').'
'.&mt('Review the identified questions, choose which to convert, and (optionally) override the default filename to be used for each problem file.').'
'.&mt('Complete the import of questions.').'
');
}
# ---------------------------------------------------------------- Display One
sub display_one {
my ($r,$fn,$page,$textref,$header) = @_;
my %topics;
$topics{2} = &mt('Select the format of the question number - e.g., 1, 1., 1), (1 or (1) - ').'
'."\n";
$topics{3} = &mt('Indicate the number of blocks of different question types in the testbank file.').' ';
$r->print('
'.&mt('Identification of blocks of questions').'
'."\n".
'');
return;
}
# ---------------------------------------------------------------- Display Two
sub display_two {
my ($r,$fn,$page,$textref,$header,$qcount) = @_;
my $blocks = $env{'form.blocks'};
my $qnumformat = $env{'form.qnumformat'};
my @types = ("MC","MA","TF","Ess","FIB","Ord");
my %typenames = &Apache::lonlocal::texthash(
MC => "Multiple Choice",
TF => "True/False",
MA => "Multiple Answer",
Ess => "Essay",
FIB => "Fill-in-the-blank",
Ord => "Ranking/ordering",
);
my %qnumtypes = (
number => "1",
period => "1.",
paren => "(1)",
leadparen => "(1",
trailparen => "1)",
);
my $bl1st = '';
my $bl1end = '';
if ($blocks == 1) {
$bl1st = '1';
$bl1end = $qcount;
}
my $steptitle = &mt('Information about question types and formats in each block.');
$r->print('
'.&mt('Classification of blocks').'
'.
'');
return;
}
# ---------------------------------------------------------------- Display Three
sub display_three {
my ($r,$fn,$page,$textref,$res,$header,$webpath,$qcount) = @_;
my $qnumformat = $env{'form.qnumformat'};
my $filename = $env{'form.filename'};
my $source = $env{'form.go'};
my $blocks = $env{'form.blocks'};
my ($alphabet,$romans) = &get_constants();
my @start = ();
my @end = ();
my @nums = ();
my @qtype = ();
my @foilformats = ();
my @ansrtypes = ();
my %multparts = ();
my $numitems = 0;
my %lt = &Apache::lonlocal::texthash (
crt => 'Create?',
typ => 'Type',
fnam => 'Filename',
ques => 'Question',
answ => 'Answer',
chka => 'check all',
unch => 'uncheck all',
);
for (my $i=0; $i<$blocks; $i++) {
if (($env{"form.start_$i"} ne '') && ($env{"form.end_$i"} ne '')) {
$start[$i] = $env{"form.start_$i"};
$end[$i] = $env{"form.end_$i"};
$nums[$i] = $end[$i]-$start[$i] +1;
$qtype[$i] = $env{"form.qtype_$i"};
if (($qtype[$i] eq "MC") || ($qtype[$i] eq "MA") || ($qtype[$i] eq "Ord")) {
$foilformats[$i] = $env{"form.foilformat_$i"};
} else {
$foilformats[$i] = '';
}
if (($qtype[$i] eq "MA") || ($qtype[$i] eq "FIB") || ($qtype[$i] eq "TF") || ($qtype[$i] eq "Ord")) {
$ansrtypes[$i] = $env{"form.ansr_$i"};
} else {
$ansrtypes[$i] = '';
}
} else {
$nums[$i] = 0;
}
$numitems += $nums[$i];
}
my ($items,$ids,$footer) = &file_split(\@start,\@end,\@nums,$qnumformat,\@foilformats,$textref,\%multparts,$numitems,\@qtype,$blocks);
my ($showheader,$showcss);
if ($res eq 'application/rtf' || $res eq 'text/html') {
if ($header ne '') {
$showheader = &HTML::Entities::decode($header);
if ($res eq 'text/html') {
$showheader = &build_image_url($webpath,$showheader);
}
}
}
$r->print('
'.&mt('Review and selection of problems to convert').'
'."\n".
'');
}
# ---------------------------------------------------------------- Final Display
sub final_display {
my ($r,$fn,$page,$textref,$res,$header,$css,$js,$webpath,$dirpath,$subdir) = @_;
my $qnumformat = $env{'form.qnumformat'};
my $blocks = $env{'form.blocks'};
my $question_id = '';
my @question_title = ();
my @question_status = ();
my @qtype = ();
my @start = ();
my @nums = ();
my @end = ();
my @foilformats = ();
my @ansrtypes = ();
my %multparts = ();
my $numitems = 0;
my @createprobs = &Apache::loncommon::get_env_multiple('form.createprob');
for (my $i=0; $i<$blocks; $i++) {
$start[$i] = $env{"form.start_$i"};
$end[$i] = $env{"form.end_$i"};
if (($end[$i] - $start[$i]) >= 0) {
$nums[$i] = $end[$i] - $start[$i]+1;
} else {
$nums[$i] = 0;
}
$qtype[$i] = $env{"form.qtype_$i"};
if (($qtype[$i] eq "MC") || ($qtype[$i] eq "MA") || ($qtype[$i] eq "Ord")) {
$foilformats[$i] = $env{"form.foilformat_$i"};
} else {
$foilformats[$i] = '';
}
if (($qtype[$i] eq "MA") || ($qtype[$i] eq "FIB") || ($qtype[$i] eq "TF") || ($qtype[$i] eq "Ord")) {
$ansrtypes[$i] = $env{"form.ansr_$i"};
}
$numitems += $nums[$i];
}
my %answers;
my ($items,$ids,$footer) = &file_split(\@start,\@end,\@nums,$qnumformat,\@foilformats,$textref,\%multparts,$numitems,\@qtype,$blocks);
# Converting MC and MA answer to number, and splitting answers for FIB, and ordering for Ord.
my ($alphabet,$romans) = &get_constants();
my %patterns = (
comma => ',',
space => '\s+',
line => '[\r\n\f]+',
tab => '\t+',
);
for (my $i=0; $i<$blocks; $i++) {
if ($nums[$i] > 0) {
if (($qtype[$i] eq "MC") || ($qtype[$i] eq "MA") || ($qtype[$i] eq "FIB") || ($qtype[$i] eq "Ord")) {
for (my $k=$numitems+$start[$i]-1; $k<$numitems+$end[$i]; $k++) {
my $qnum = $k - $numitems;
next if (!grep(/^$qnum$/,@createprobs));
if (($res eq 'application/rtf') || ($res eq 'text/html')) {
$items->[$k] = &HTML::Entities::decode($items->[$k]);
}
@{$answers{$qnum}} = ();
if ($qtype[$i] eq "MC") {
$items->[$k] =~ tr/A-Z/a-z/;
$items->[$k] =~ s/<\/?[^>]+>//g;
$items->[$k] =~ s/\W//g;
if ($foilformats[$i] eq "lcspace" || $foilformats[$i] eq "ucspace" || $foilformats[$i] eq "lcperiod" || $foilformats[$i] eq "lcparen" || $foilformats[$i] eq "lconeparen" || $foilformats[$i] eq "lcdotparen" || $foilformats[$i] eq "ucparen" || $foilformats[$i] eq "ucperiod" || $foilformats[$i] eq "uconeparen" || $foilformats[$i] eq "ucdotparen") {
for (my $j=0; $j<@{$alphabet}; $j++) {
if ($alphabet->[$j] eq $items->[$k]) {
push @{$answers{$qnum}}, $j;
last;
}
}
} elsif (($foilformats[$i] eq "romparen") || ($foilformats[$i] eq "romperiod") || ($foilformats[$i] eq "romoneparen") || ($foilformats[$i] eq "romdotparen")) {
for (my $j=0; $j<@{$romans}; $j++) {
if ($romans->[$j] eq $items->[$k]) {
push @{$answers{$qnum}}, $j;
last;
}
}
}
} elsif (($qtype[$i] eq "MA") || ($qtype[$i] eq "Ord")) {
$items->[$k] =~ tr/A-Z/a-z/;
$items->[$k] =~ s/<\/?[^>]+>//g;
my @corrects = split/$patterns{$ansrtypes[$i]}/,$items->[$k];
foreach my $correct (@corrects) {
my @tied;
if ($qtype[$i] eq "Ord") {
if ($correct =~ /=/) {
@tied = split(/=/,$correct);
for (my $j=0; $j<@tied; $j++) {
$tied[$j] =~ s/\W//g;
}
} else {
$correct =~s/\W//g;
}
} else {
$correct =~s/\W//g;
}
if ($foilformats[$i] eq "lcspace" || $foilformats[$i] eq "ucspace" || $foilformats[$i] eq "lcperiod" || $foilformats[$i] eq "lcparen" || $foilformats[$i] eq "ucparen" || $foilformats[$i] eq "ucperiod") {
if (($qtype[$i] eq "Ord") && (@tied > 0)) {
my @ties;
foreach my $tie (@tied) {
for (my $j=0; $j<@{$alphabet}; $j++) {
if ($alphabet->[$j] eq $tie) {
push(@ties,$j);
last;
}
}
}
my $ans = join('=',@ties);
push(@{$answers{$qnum}},$ans);
} else {
for (my $j=0; $j<@{$alphabet}; $j++) {
if ($alphabet->[$j] eq $correct) {
push @{$answers{$qnum}}, $j;
last;
}
}
}
} elsif (($foilformats[$i] eq "romparen") || ($foilformats[$i] eq "romperiod") || ($foilformats[$i] eq "romoneparen") || ($foilformats[$i] eq "romdotparen")) {
if (($qtype[$i] eq "Ord") && (@tied > 0)) {
my @ties;
foreach my $tie (@tied) {
for (my $j=0; $j<@{$romans}; $j++) {
if ($romans->[$j] eq $tie) {
push(@ties,$j);
last;
}
}
}
push(@{$answers{$qnum}},join('=',@ties));
} else {
for (my $j=0; $j<@{$romans}; $j++) {
if ($romans->[$j] eq $correct) {
push @{$answers{$qnum}}, $j;
last;
}
}
}
}
}
} elsif ($qtype[$i] eq "FIB") {
$items->[$k] =~ s/<\/?[^>]+>//g;
@{$answers{$qnum}} = split/$patterns{$ansrtypes[$i]}/,$items->[$k];
for (my $j=0; $j<@{$answers{$qnum}}; $j++) {
$answers{$qnum}[$j] =~ s/^\s+//;
$answers{$qnum}[$j] =~ s/\s+$//;
if ($j==0) {
$answers{$qnum}[$j] =~ s/^<[^>]+>//;
} elsif ($j == @{$answers{$qnum}}-1) {
$answers{$qnum}[$j] =~ s/<\/[^>]+>$//;
}
}
}
}
}
}
}
my $state;
$r->print('');
} elsif (($destdir ne '') && (-e $destdir)) {
my (@qn_file,@result,@numid);
my $qcount = 0;
my $itemcount = 0;
for (my $i=0; $i<$blocks; $i++) {
if ($nums[$i] > 0) {
if (($qtype[$i] eq "MC") || ($qtype[$i] eq "MA") || ($qtype[$i] eq "FIB") || ($qtype[$i] eq "Ord")) {
for (my $j=$start[$i]-1; $j<$end[$i]; $j++) {
$numid[$qcount] = $ids->[$itemcount];
$itemcount ++;
next if (!grep(/^$qcount$/,@createprobs));
my $libfile = &probfile_name($j);
my $answer = $j + $numitems;
my $numans = scalar(@{$answers{$qcount}});
my $foilcount = 0;
if (($qtype[$i] eq "MC") || ($qtype[$i] eq "MA") || ($qtype[$i] eq "Ord")) {
$foilcount = @{$multparts{$j}};
$foilcount --;
}
($result[$qcount],$qn_file[$qcount]) = &create_mcq($destdir,$subdir,\@{$multparts{$j}},\@{$answers{$qcount}},$qtype[$i],$libfile,$res,$header,$footer,$js,$css);
$qcount ++;
}
} elsif ($qtype[$i] eq "TF") {
for (my $j=$start[$i]-1; $j<$end[$i]; $j++) {
$numid[$qcount] = $ids->[$itemcount];
$itemcount ++;
next if (!grep(/^$qcount$/,@createprobs));
my $libfile = &probfile_name($j);
my $answer = $j + $numitems;
$items->[$answer] =~ s/^\s+//;
$items->[$answer] =~ s/\s+$//;
$items->[$answer] =~ s/\W//g;
$items->[$answer] =~ tr/A-Z/a-z/;
my $answer_id = '';
if ($ansrtypes[$i] eq 'word' ) {
if ($items->[$answer] =~ m/true/) {
$answer_id = 0;
} else {
$answer_id = 1;
}
} elsif ($ansrtypes[$i] eq 'lett') {
if ($items->[$answer] =~ m/^t/) {
$answer_id = 0;
} else {
$answer_id = 1;
}
}
($result[$qcount],$qn_file[$qcount]) = &create_ess($destdir,$subdir,$answer_id,$items->[$j],$items->[$answer],$qtype[$i],$libfile,$res,$header,$footer,$js,$css);
$qcount ++;
}
} elsif ($qtype[$i] eq "Ess") {
for (my $j=$start[$i]-1; $j<$end[$i]; $j++) {
$numid[$qcount] = $ids->[$itemcount];
$itemcount ++;
next if (!grep(/^$qcount$/,@createprobs));
my $libfile = &probfile_name($j);
my $answer = $j + $numitems;
my $answer_id = '';
($result[$qcount],$qn_file[$qcount]) = &create_ess($destdir,$subdir,$answer_id,$items->[$j],$items->[$answer],$qtype[$i],$libfile,$res,$header,$footer,$js,$css);
$qcount ++;
}
}
}
}
my ($successes,$failures,$existing);
for (my $i=0; $i<@qn_file; $i++) {
if ($result[$i] eq 'ok') {
$successes .= ''.$numid[$i].': '.
$qn_file[$i].' '."\n";
} elsif ($result[$i] eq 'failed') {
$failures .= $numid[$i].': '.$qn_file[$i].' '."\n";
} elsif ($result[$i] eq 'exists') {
$existing .= ''.$numid[$i].': '.
$qn_file[$i].' '."\n";
}
}
if ($successes) {
$r->print('
'.&mt('Individual problem files have been created from the following problems included in the testbank file:').' '.$successes.'
'.
&mt('The problems must be published before they can be used in a course').'
');
}
if ($failures) {
$r->print('
'.&mt('An error occurred when opening files for the following problems, so they have not been created:').' '.$failures.'
');
}
if ($existing) {
$r->print('
'.&mt('The following files already existed, and were not overwritten so these problems generated from the testbank have not been saved:').' '.$existing.'