#!/usr/bin/perl # # $Id: testlib.pl,v 1.53 2006/11/10 00:52:01 ddoughty Exp $ # 12/31/01 merged various changes from production site, marked with ##wac # Source File: testlib.pl use Data::Dumper; use MIME::Base64 qw(encode_base64 decode_base64) ; require 'genutil.pl'; %TEST_STATES = ( _PENDING => 0, _IN_PROGRESS => 1, _PAUSED_BY_USER => 2, _DECLINED => 3, _TIME_EXPIRED => 4, _TERMINATED => 5, _COMPLETED => 6 ); %TEST_STATE_DESCRIPTION = ( '0' => 'Pending', '1' => 'In Progress', '2' => 'Paused by User', '3' => 'Confidentiality Declined', '4' => 'Time Expired', '5' => 'Terminated by Administrator', '6' => 'Completed' ); %TEST_SEGMENT_DESCRIPTION = ( '0' => 'Confidentiality', '1' => 'Pretest Survey/Profile', '2' => 'Core Test', '3' => 'Posttest Profile', '4' => 'Posttest Survey'); my $HBI_Debug_redirect = 0 ; # The following variables are used for formatting email messages. # The email creation and formats of data in the emails # is not strictly modular. On the down side these values # are global, created, and used ala side effects. On the # plus side, flexibility is gained. The variables will all # be multi-line strings. $MIME_start will be used once and only # once in each email message, at the very beginning of the email # message. Its purpose is to tell the email client that the email # has different sections, with potentially different kinds of data. # $mm_7bit_text and $mm_encoded_html go at the front of a section # with its kind of data. $mm_7bit_text is normal 7-bit ASCII # characters without any special formatting. $mm_encoded_html # is for base64 uuencoded HTML data. $MIME_start = "" ; $mm_7bit_text = "" ; $mm_encoded_html = ""; sub remove_pending_tests { my ($clid, $target_cndid) = @_; opendir(DIR, $testpending); @dots = readdir(DIR); closedir DIR; foreach $rmfile (@dots) { if ($rmfile =~ /^$clid\.$target_cndid\./ ) { $ulinkfile = join($pathsep, $testpending, $rmfile); $cnt = unlink $ulinkfile; } } @dots = (); } #hkh 01/04 remove test in progress - indicated by '*' at end of $atest sub remove_inprogtest { my ($clid, $target_cndid, $authtests) = @_; opendir(DIR, $testinprog); @dots = readdir(DIR); closedir DIR; $chgauthtests = "N"; @atests = split(/\;/, $authtests); foreach $rmfile (@dots) { $match = "N"; if ($rmfile =~ /^$clid\.$target_cndid\./ ) { foreach $atest (@atests) { if ($atest =~ /\*/) { $_ = $atest; s/\*//; if (($rmfile =~ /^$clid\.$target_cndid\.$_/) || ($rmfile =~ /^$clid\.$target_cndid\.$_.tim/)) { $match = "Y"; } } } if ($match eq "N") { $ulinkfile = join($pathsep, $testinprog, $rmfile); $cnt = unlink $ulinkfile; $chgauthtests = "Y"; } } } @dots = (); @atests = (); } #hkh 01/04 only remove tests in PENDING directory if they were removed in # candidate registration screen. sub remove_pending_oldtests { my ($clid, $target_cndid, $authtests) = @_; opendir(DIR, $testpending); @dots = readdir(DIR); closedir DIR; my $filename = "tests.$clid"; my @lines = &get_data($filename); foreach $i (@lines) { my @banana = split('&', $i); my $funkey = &get_a_key($filename, $banana[0], "availto"); $funkey =~ s/\./ /; if ($funkey eq '') { #If funkey eq Y, that means that it IS selfreg. But we want it to find things that are NOT selfreg. $funkey = "Y "; } else { $funkey =~ /^\w\s/; $funkey = $&; } if ($funkey eq "N ") { my $pendofile = "../secure_html/tests/pending/$clid.$cndid.$banana[0]"; my $pwd = `pwd`; if ( -e $pendofile) { if ($authtests ne '') { $authtests .= "\;"; } $authtests .= "$banana[0]"; } } } @atests = split(/\;/, $authtests); foreach $rmfile (@dots) { $match = "N"; if ($rmfile =~ /^$clid\.$target_cndid\./ ) { foreach $atest (@atests) { if ($rmfile =~ /^$clid\.$target_cndid\.$atest/) { $match = "Y"; last; } } if ($match eq "N") { $ulinkfile = join($pathsep, $testpending, $rmfile); $cnt = unlink $ulinkfile; $chgauthtests = "Y"; } } } @dots = (); @atests = (); } #hkh 01/04 add new tests added in candidate registration (--->) sub create_newtests_list { my ($clid, $target_cndid, $authtests) = @_; opendir(DIR, $testpending); my @dots = readdir(DIR); closedir DIR; @newtests = (); my @atests = split(/\;/, $authtests); foreach $atest (@atests) { if ($atest ne '') { $match = "N"; foreach $rmfile (@dots) { if ($rmfile =~ /^$clid\.$target_cndid\.$atest/) { $match = "Y"; } else { if ($atest=~ /\*/) { $match = "Y"; } } } if ($match eq "N") { push(@newtests, $atest); } } } #hkh 01/04 if nothing is changed on cand. reg. screen, do not pop-up 'Tests # Registered' message if (($#newtests == -1) && ($chgauthtests eq "N")) { $FORM{'respmsg'} = ""; } @dots = (); @atests = (); return @newtests; } sub get_pending_tests { my ($clid, $target_cndid, $opts) = @_; return &get_tests($clid, $target_cndid, $testpending, $opts); } sub get_inprog_tests { my ($clid, $target_cndid, $opts) = @_; return &get_tests($clid, $target_cndid, $testinprog, $opts); } sub get_completed_tests { my ($clid, $target_cndid, $opts) = @_; return &get_tests($clid, $target_cndid, $testcomplete, $opts); } sub get_tests { my ($clid, $target_cndid, $testdir, $opts) = @_; opendir(DIR, $testdir); my @files = readdir(DIR); closedir DIR; $authtests = ""; foreach $file (@files) { if ($file =~ /^$clid\.$target_cndid\.(\S+)$/ and $file !~ /\.tim$/) { my $testid = $1; $bob=&within_availability_window($clid, $testid, time); $bobt=time; if ( ! $opts->{restrict_to_availability_window} || &within_availability_window($clid, $testid, time) ) { $authtests = join(';', $authtests, $testid); } } } return $authtests; } # # @filelist = &get_test_result_files($directory, $clid, $testid); # # Return: List of matching files, or undef if there was an error. # sub get_test_result_files { my ($dir, $clid, $testid) = @_; if ( ! defined($clid) ) { &logger::logerr("Undefined client ID for directory '$dir', testid '$testid'"); return undef; } if ( ! defined($testid) ) { &logger::logerr("Undefined test ID for directory '$dir', client ID '$clid'"); return undef; } return get_matching_files($dir, "^$clid".'\.\S+\.'."$testid\$"); } # # @filelist = &get_cnd_result_files($directory, $clid, $cndid); # # Return: List of matching files, or undef if there was an error. # sub get_cnd_result_files { my ($dir, $clid, $cndid) = @_; if ( ! defined($clid) ) { &logger::logerr("Undefined client ID for directory '$dir', cndid '$cndid'"); return undef; } if ( ! defined($cndid) ) { &logger::logerr("Undefined cnd ID for directory '$dir', client ID '$clid'"); return undef; } return get_matching_files($dir, "^$clid".'\.'."$cndid".'\.\S+$'); } # # @filelist = &get_matching_files($directory, $regex); # # Return: List of matching files, or undef if there was an error. # sub get_matching_files { my ($dir, $regex) = @_; if ( ! defined($dir) ) { &logger::logerr("Undefined directory for client ID '$clid', cndid '$cndid'"); return undef; } if ( ! opendir (GDIR, $dir) ) { &logger::logerr("Unable to open directory '$dir' for reading: $!"); return undef; } my @filenames = readdir(GDIR); closedir GDIR; my @filelist = (); foreach $file (sort @filenames) { if (($file =~ /$regex/i )) { push @filelist, $file; } } my @converter; if ($SESSION{'uid'} ne '') { my $imaregistrar = &get_a_key("cnd.$CLIENT{'clid'}", $SESSION{'uid'}, "registrar"); if ($imaregistrar eq 'Y') { foreach $rotator (@filelist) { my @cnd = split(/\./, $rotator); my $creator = &get_a_key("cnd.$CLIENT{'clid'}", $cnd[1], "createdby"); push(@converter, $rotator) unless $creator ne $SESSION{'uid'}; } @filelist = @converter; } } else { &logger::logerr("No SESSION{uid} set!"); } return @filelist; } sub get_test_sequence { $pathpassed = ($#_ == 3) ? 1 : 0; &get_test_profile($_[0], $_[2]); if ($pathpassed) { $trash2 = join($pathsep, "$_[3]", "$_[0].$_[1].$_[2]"); } else { $trash1 = join($pathsep, $testpending, "$_[0].$_[1].$_[2]"); $trash2 = join($pathsep, $testinprog, "$_[0].$_[1].$_[2]"); $trash3 = join($pathsep, $testcomplete, "$_[0].$_[1].$_[2]"); } $msg = ""; open(TESTFILE, "<$trash2") or $msg="failed"; if (($msg eq 'failed') && ($pathpassed == 0)) { $msg = ""; open(TESTFILE,"<$trash1") or $msg="failed"; if ($msg eq 'failed') { $msg = ""; open(TESTFILE,"<$trash3") or $msg="failed"; } } if ($msg eq "failed") { $msg = ""; } else { @seqlines = ; close TESTFILE; $isubtest = 1; $iidx = 0; $iaryidx = 1; foreach $seqline (@seqlines) { chop ($seqline); if ($iidx eq 0) { @status = split(/&/, $seqline); $ifld = 0; $TEST_SESSION{'clid'} = $status[$ifld++]; $TEST_SESSION{'uid'} = $status[$ifld++]; $TEST_SESSION{'tstid'} = $status[$ifld++]; $TEST_SESSION{'state'} = $status[$ifld++]; $TEST_SESSION{'dscl'} = $status[$ifld++]; $TEST_SESSION{'profb'} = $status[$ifld++]; $TEST_SESSION{'id'} = $status[$ifld++]; $TEST_SESSION{'profa'} = $status[$ifld++]; $TEST_SESSION{'srvy'} = $status[$ifld++]; $TEST_SESSION{'ntfy'} = $status[$ifld++]; $TEST_SESSION{'emlcnd'} = $status[$ifld++]; @status = (); $iidx++; } else { if ($iaryidx eq 1) {$SUBTEST_QUESTIONS{$isubtest} = $seqline;} if ($iaryidx eq 2) {$SUBTEST_ANSWERS{$isubtest} = $seqline;} if ($iaryidx eq 3) {$SUBTEST_RESPONSES{$isubtest} = $seqline;} if ($iaryidx eq 4) {$SUBTEST_SUMMARY{$isubtest} = $seqline;} $iaryidx++; if ($iaryidx eq 5) { $iaryidx = 1; $isubtest++; } } } } @seqlines = (); return; } sub get_test_sequence_from_history { my ($dir,$clid,$cndid,$tstid,$testdate) = @_; my $testseconds = toGMSeconds($testdate); my @seqlines = (); &get_test_profile($clid, $tstid); my $trash = join($pathsep, $dir, "$clid.$tstid.history"); $msg = ""; open(TESTFILE, "<$trash") or $msg="failed to open history file"; if ($msg eq "failed") { $msg = ""; } else { @seqlines = ; close TESTFILE; my @histentries; foreach (@seqlines) { my ($timestamp,$trash) = split(/\<\<\>\>/, $_); $timestamp = toGMSeconds($timestamp); if (abs($testseconds-$timestamp) < 5 && $trash =~ "^$clid\&$cndid\&$tstid\&.*") { push @histentries, $_; } } if (not @histentries) { # No entry in History file return 0; } #### #my $sgrepfor = "^$testdate\<\<\>\>$clid\&$cndid\&$tstid\&(.*.)"; #my @histentries = grep(/$sgrepfor/,@seqlines); #if ($histentries[0] == "") { # # strip "GMT" and try again # my $testdate0 = $testdate; # $testdate0 =~ s/ GMT//g; # my $sgrepfor = "^$testdate0\<\<\>\>$clid\&$cndid\&$tstid\&(.*.)"; # my @histentries = grep(/$sgrepfor/,@seqlines); #} #if ($histentries[0] == "") { # # convert date to old format and try yet again # my %months = ("Jan" => 1, "Feb" => 2, "Mar" => 3, # "Apr" => 4, "May" => 5, "Jun" => 6, # "Jul" => 7, "Aug" => 8, "Sep" => 9, # "Oct" => 10, "Nov" => 11, "Dec" => 12); # my @datearray = split(/ /, $testdate); # my ($day, $month, $year) = split(/-/, $datearray[0]); # $datearray[0] = "$year-$months{$month}-$day"; # $testdate = join(" ", @datearray); # my $sgrepfor = "^$testdate\<\<\>\>$clid\&$cndid\&$tstid\&(.*.)"; # @histentries = grep(/$sgrepfor/,@seqlines); # @datearray = (); #} #if ($histentries[0] == "") { # # No entry in History file # return 0; #} #### @seqlines = split(/\<\<\>\>/, $histentries[0]); my @status = split(/&/, $seqlines[1]); my $ifld = 0; $TEST_SESSION{'clid'} = $status[$ifld++]; $TEST_SESSION{'uid'} = $status[$ifld++]; $TEST_SESSION{'tstid'} = $status[$ifld++]; $TEST_SESSION{'state'} = $status[$ifld++]; $TEST_SESSION{'dscl'} = $status[$ifld++]; $TEST_SESSION{'profb'} = $status[$ifld++]; $TEST_SESSION{'id'} = $status[$ifld++]; $TEST_SESSION{'profa'} = $status[$ifld++]; $TEST_SESSION{'srvy'} = $status[$ifld++]; $TEST_SESSION{'ntfy'} = $status[$ifld++]; $TEST_SESSION{'emlcnd'} = $status[$ifld++]; @status = (); $SUBTEST_QUESTIONS{2} = $seqlines[2]; $SUBTEST_ANSWERS{2} = $seqlines[3]; $SUBTEST_RESPONSES{2} = $seqlines[4]; $SUBTEST_SUMMARY{2} = $seqlines[5]; } @seqlines = (); return 1; } sub promote_test_sequence { $ffrom = join($pathsep, $_[0], "$TEST_SESSION{'clid'}.$TEST_SESSION{'uid'}.$TEST_SESSION{'tstid'}"); # open(TESTFILE, "<$ffrom") or $msg="failed"; #&dbgprint("promote_test_sequence($_[0]):$_[1]:$_[2]\n"); open(TESTFILE, "<$ffrom") or return; #&dbgprint("\t$ffrom:-------:$msg\n"); @seqlines = ; close TESTFILE; @tsflds = split(/\./, $TEST_SESSION{'state'}); $TEST_SESSION{'state'} = "$_[2].$tsflds[1].$tsflds[2]"; @tsflds = (); $hdr = $TEST_SESSION{'clid'}; $hdr = join('&', $hdr, $TEST_SESSION{'uid'}); $hdr = join('&', $hdr, $TEST_SESSION{'tstid'}); $hdr = join('&', $hdr, $TEST_SESSION{'state'}); $hdr = join('&', $hdr, $TEST_SESSION{'dscl'}); $hdr = join('&', $hdr, $TEST_SESSION{'profb'}); $hdr = join('&', $hdr, $TEST_SESSION{'id'}); $hdr = join('&', $hdr, $TEST_SESSION{'profa'}); $hdr = join('&', $hdr, $TEST_SESSION{'srvy'}); $hdr = join('&', $hdr, $TEST_SESSION{'ntfy'}); $hdr = join('&', $hdr, $TEST_SESSION{'emlcnd'}); $fto = join($pathsep, $_[1], "$TEST_SESSION{'clid'}.$TEST_SESSION{'uid'}.$TEST_SESSION{'tstid'}"); open(TESTFILE, ">$fto") or $msg="failed"; print TESTFILE "$hdr\n"; for $iidx (1 .. $#seqlines) { print TESTFILE "$seqlines[$iidx]"; } close TESTFILE; $chmodok = chmod 0666, $fto; $cnt = unlink $ffrom; @seqlines=(); #&dbgprint("\t$ffrom:$fto:$msg\n"); } sub summarize_survey { } sub summarize_test { my $returnval=""; # compute score # HBI This subroutine is grading the test. $SUBTEST_RESPONSES{$_[0]} =~ s/\'//g; #print STDERR "summarize_test($_[0]):$SUBTEST{'id'}:$SUBTEST{'scr'}\n"; #&dbgprint("summarize_test($_[0]):$SUBTEST{'id'}:$SUBTEST{'scr'}\n"); #&dbgprint("\t:$SUBTEST_ANSWERS{$_[0]}\n\t:$SUBTEST_RESPONSES{$_[0]}\n"); # warn "SUBTEST_ANSWERS:$SUBTEST_ANSWERS{$_[0]}\n" ; # warn "SUBTEST_RESPONS:$SUBTEST_RESPONSES{$_[0]}\n" ; if ($SUBTEST{'scr'} eq '3') { $msg = "You have completed this unscored portion of the test.
\n"; $msg = join("", $msg, "Click the Continue button below to proceed.
\n"); $SUBTEST{'score'} = $msg; $msg = ""; $summary = "Not Scored by Definition"; $returnval="u"; } else { @cans = split(/&/, $SUBTEST_ANSWERS{$_[0]}); # The format of an element of @cans is "answer::subject:weight:points:deduction" # The default value for weight is one, for points is 100 for the entire test, # The default for deduction is 0. @crsp = split(/&/, $SUBTEST_RESPONSES{$_[0]}); # The format of an element of @crsp is "response::comments" # HBI patterns for scoring - # ($cans =~ /[0-9]=[0-1]/ ) - Answers are patterns of selected or unselected for multiple selection. # - separated by question marks, like 0=1?1=1?2=0 the first digit may be in any order. # ($cans =~ /[anorR]\./ ) # In get_label_index , the letters rR are used for Roman Numerals, Lower and Uppercase, respectively. # In get_label_index , the letter n is used for Arabic Numerals, 1, 2, 3, etc. # In get_label_index , the letters aA are used for letters; a,b,c, etc. ; Lower and Uppercase, respectively. # ($cans =~ m/\,/) (If there is a comma, then there are multiple correct answers, and anyone earns the score. # $iscorrect = ($cans eq $crsp) ? 1 : 0; # Looks to see if the answer matches the response. # Builds the variable $byquestion. $correct = 0; $incorrect = 0; $totans = 0; $byquestion = ""; #&dbgprint("\t:261:$#cans:$#crsp\n"); for (1 .. $#cans) { $ansmask = ""; ($cans, $scoring) = split(/::/, $cans[$_]); ($scsubj, $scwght, $scpts, $scded) = split(/:/, $scoring); unless ($scwght) { $scwght = 1;} unless ($scpts) { $scpts = 100 / $#cans;} unless ($scded) { $scded = 0;} $cans = lc($cans); ($crsp,$ccmts) = split(/::/, lc($crsp[$_])); #&dbgprint("\t:271:$_:$cans:$crsp:$ccmts\n"); $crsp =~ s/\'//; if ($cans =~ /[0-9]=[0-1]/ ) { @ansopts = split(/\?/, $cans); shift @ansopts; for (0 .. $#ansopts) { $ansdig = ($ansopts[$_] =~ /=1/ ) ? "$_" : "xxx" ; ### DED-07 7/18/2002 #$ansmask = join('', $ansmask, $ansdig); $ansmask = join('?', $ansmask, $ansdig); } #$ansmask =~ s/x//g; #$crsp =~ s/x//g; $iscorrect = ($ansmask eq $crsp) ? 1 : 0; $byquestion = join('/', $byquestion, "$iscorrect.$ansmask.$crsp"); #&dbgprint("\t:284:$_:$#ansopts:$ansmask:$crsp\n"); } elsif ($cans =~ /[anorR]\./ ) { @ansopts = split(/\./, $cans); $anstype = shift @ansopts; if ($anstype eq 'o') { foreach $ansopt (@ansopts) { $ansopt++; ### DED 7/17/2002 # $ansmask = join('',$ansmask, $ansopt); $ansmask = join('?',$ansmask, $ansopt); } } else { @albls=&set_answer_labels($anstype); for (0 .. $#ansopts) { $cansord[$ansopts[$_]] = $albls[$_]; } foreach $cansord (@cansord) { ### DED 7/17/2002 #$ansmask = join('', $ansmask, $cansord); $ansmask = join('?', $ansmask, $cansord); } @cansord = (); } #&dbgprint("\t:303:$_:$#ansopts:$anstype:$asnmask:$crsp\n"); $iscorrect = ($ansmask eq $crsp) ? 1 : 0; $byquestion = join('/', $byquestion, "$iscorrect.$ansmask.$crsp"); } elsif ($cans =~ m/\,/) { @ansopts = split(/\,/,$cans); $iscorrect = 0; foreach $ansopt (@ansopts) { if ($crsp eq $ansopt) { $iscorrect = 1; } } #&dbgprint("\t:314:$_:$#ansopts:$crsp\n"); $byquestion = join('/', $byquestion, "$iscorrect.$cans.$crsp"); } else { $iscorrect = ($cans eq $crsp) ? 1 : 0; #&dbgprint("\t:318:$_:$cans:$crsp\n"); $byquestion = join('/', $byquestion, "$iscorrect.$cans.$crsp"); } if ($SUBTEST{'scr'} eq '1') { # weighted $correct += ($iscorrect) ? $scwght : 0; $incorrect += ($iscorrect) ? 0 : $scwght; $totans += $scwght; } elsif ($SUBTEST{'scr'} eq '2') { # cummulative $correct += ($iscorrect) ? $scpts : 0; ##wac v 01/04/02 this code was not scoring cummulative properly, remove 2 lines, added 1 #remove this: $correct -= ($iscorrect) ? 0 : $scded; #remove this $incorrect += ($iscorrect) ? 0 : $scpts; # add next line, don't know why it referred to $scpts. $incorrect += ($iscorrect) ? 0 : $scded; ##wac ^ $totans += $scpts; } else { # percent and default $totans++; $correct += ($iscorrect) ? 1 : 0; $incorrect += ($iscorrect) ? 0 : 1; } @ansopts = (); } if ($totans == 0) { $totans = 1; } if ($SUBTEST{'scr'} eq '1') { # weighted $score = int(($correct * 100) / $totans); $scpassing = $SUBTEST{'minpass'}; } elsif ($SUBTEST{'scr'} eq '2') { # cummulative $score = ($correct - $incorrect); $scpassing = ($SUBTEST{'minpass'} / 100) * $totans; } else { $score = int(($correct * 100) / $totans); $scpassing = $SUBTEST{'minpass'}; } @cans = (); @crsp = (); $SUBTEST{'correct'} = $correct; $SUBTEST{'incorrect'} = $incorrect; $SUBTEST{'score'} = $score; if ((defined $scpassing) and ($scpassing ne "") and ($score >= $scpassing)) { # Passed. $SUBTEST{'scorebar'} = "greenbar.jpg" ; $returnval = "p" ; } else { # Failed. $SUBTEST{'scorebar'} = "redbar.jpg" ; $returnval = "f" ; } $SUBTEST{'scorebarwidth'} = ($score * 3); $summary = join( '&', $SUBTEST{'correct'}, $SUBTEST{'incorrect'}); $summary = join( '&', $summary, $SUBTEST{'score'}, $SUBTEST{'scorebar'}); $summary = join( '&', $summary, $SUBTEST{'scorebarwidth'}); if ($FORM{'submit'} eq 'timeexpired') { $summary = join( '&', $summary, "TIME EXPIRED"); } $summary = join( '&', $summary, $byquestion); } warn "summarize_test RESULTS correct $correct incorrect $incorrect score $score scpassing $scpassing returnval $returnval \n" ; warn "summarize_test summary $summary \n" ; $SUBTEST_SUMMARY{$_[0]} = $summary; $summary = ""; $score = ""; return $returnval; } # ($tsubtest) # remt # 0 Never # 1 On Posting of Answer # 2 Cumulative At End # 3 With Question # rema # 0 Not Applicable # 1 Incorrect Answers # 2 Correct Answers # 3 Both sub remediate_summary { $remediationtext=""; if (($SUBTEST{'remt'} eq '2') && ($SUBTEST{'rema'} ne '0') && ($SUBTEST{'scr'} ne '3')) { @tqnos = split(/&/, $SUBTEST_QUESTIONS{$_[0]}); @qrcans = split(/&/, $SUBTEST_ANSWERS{$_[0]}); # jharding, 2004-06-22, corrected the retrieval of $byquestion # for tests that are timed out. BUG 184. @summary = split(/&/, $SUBTEST_SUMMARY{$_[0]}); $correct = $summary[0]; $incorrect = $summary[1]; $score = $summary[2]; $scorebar = $summary[3]; $scorebarwidth = $summary[4]; if ($#summary eq '5') { $byquestion = $summary[5]; } else { $byquestion = $summary[6]; } @remediations = split(/\//, $byquestion); for (1 .. $#remediations) { ($cflag, $cans, $uresp) = split(/\./, $remediations[$_]); if ( ($SUBTEST{'rema'} eq '3') || (($cflag eq '0') && ($SUBTEST{'rema'} eq '1')) || (($cflag eq '1') && ($SUBTEST{'rema'} eq '2')) ) { %SQUESTION = %QUESTION; &get_question_definition($SUBTEST{'id'}, $SESSION{'clid'}, $tqnos[$_]); %TMPQUESTION = %QUESTION; %QUESTION = %SQUESTION; %SQUESTION=(); if ($SUBTEST{'rema'} eq '1') { $descriptiontext = "The following lists the questions you answered incorrectly, for your review."; } elsif ($SUBTEST{'rema'} eq '2') { $descriptiontext = "The following lists the questions you answered correctly, for your review."; } else { $descriptiontext = "The following lists the questions and answers (both correct and incorrect) for your review."; } if ($remediationtext eq '') { $remediationtext="
$descriptiontext

\n"; } ($qrcans, $trash) = split(/::/, $qrcans[$_]); $remediation = &question_remediation($_, $cans, $uresp, $qrcans, $cflag); $remediationtext = join('', $remediationtext, $remediation, "
\n"); %TMPQUESTION=(); } } if ($remediationtext eq '') { $remediationtext="
Congratulations on your perfect score.

\n"; } @remediations=(); @qrcans=(); } return $remediationtext; } sub question_remediation { $textofremediation=""; $qtxt = $TMPQUESTION{'qtx'}; $qtxt =~ s//________/g; if ($TMPQUESTION{'illustration'} eq '') { $qillus = ""; } else { $qillus = "\n$TMPQUESTION{'illustration'}
\n"; } ### DED 3/9/05 Have to split resp from comments ($_[2]) = split(/::/, $_[2]); if ($_[4]) { $ctag = "$xlatphrase[137]"; } else { $ctag = "$xlatphrase[692]"; } if ($TMPQUESTION{'qtp'} eq 'mch' ) { ### DED-05 7/17/2002 Replaced: #$quresp = $_[2]; #$qcresp = $_[1]; ### with the following to print long answers during remediation @labels=&set_answer_labels($TMPQUESTION{'qalb'}); $qanswermatch = "\ 
\n"; @txts = split(/\n/, $TMPQUESTION{'qca'}); @txts_wro = split(/\n/, $TMPQUESTION{'qia'}); @tmpquresp = split(/\?/, $_[2]); shift @tmpquresp; @ansopts = split(/\?/, $_[1]); shift @ansopts; $quresp = ""; $qcresp = "\n\n\n\n\n\n
\n"; for (0 .. $#ansopts) { $ansopt = $ansopts[$_]; $iansopt = &get_label_index($TMPQUESTION{'qalb'},$ansopt); if ($iansopt == -1) { $iansopt = 0 ; # HBI Actually an error. } $cansord[$iansopt] = $_; $qcresp = join('',$qcresp,"($ansopt) $txts[$_]
\n"); } $qcresp = join('',$qcresp, "
 \n"); for (0 .. $#cansord) { $qcresp = join('',$qcresp, "($labels[$_]) $txts_wro[$cansord[$_]]
\n"); } $qcresp = join('',$qcresp, "
\n"); for (0 .. $#tmpquresp) { ### DED-11 7/23/2002 Print " " rather than "xxx" ### for blank response (added following line) if ( $tmpquresp[$_] eq "xxx" ) { $tmpquresp[$_]=" "; } $quresp = join('',$quresp,"(",$tmpquresp[$_],") $txts[$_]
\n"); } @cansord = (); ### END DED-05 } elsif ($TMPQUESTION{'qtp'} eq 'ord' ) { ### DED-04 7/16/2002 Replaced: #$quresp = $_[2]; #$qcresp = $TMPQUESTION{'qca'}; #$qcresp =~ s/\n/
/g; ### with the following to print long answers during remediation $quresp = ""; $qcresp = ""; @tmpquresp = split(/\?/, $_[2]); shift @tmpquresp; @txts = split(/\n/, $TMPQUESTION{'qca'}); @ansopts = split(/\?/, $_[1]); shift @ansopts; for (0 .. $#ansopts) { $ansopt = $ansopts[$_]; ### DED 8/10/2002 Removed labels as "o" is used now $iansopt = $ansopt; $iansopt--; $qcresp = join('',$qcresp,"($ansopt) $txts[$iansopt]
\n"); ### DED-12 7/23/2002 Print " " rather than "xxx" ### for blank response (added following line) if ( $tmpquresp[$_] eq "xxx" ) { $tmpquresp[$_]=" "; } $quresp = join('',$quresp,"(",$tmpquresp[$_],") $txts[$iansopt]
\n"); } ### END DED-04 ### DED-13 7/30/2002 Removed following and merged mcs logic ### with mcm logic for "?" delimiter #} else { #if ($TMPQUESTION{'qtp'} eq 'mcs' ) { #@qrans=split(/\n/, $TMPQUESTION{'qia'}); #unshift @qrans, $TMPQUESTION{'qca'}; #@qrcansidx = split(/\?/, $_[3]); #shift @qrcansidx; #($qurespidx, $trash) = split(/=/, $qrcansidx[$_[2]]); #$quresp = $qrans[$qurespidx]; #$qcresp = $TMPQUESTION{'qca'}; #$qdx="\n\n"; #@qrcansidx = (); #@qrans=(); #} elsif ($TMPQUESTION{'qtp'} eq 'mcm' ) { } elsif (($TMPQUESTION{'qtp'} eq 'mcm' ) || ($TMPQUESTION{'qtp'} eq 'mcs' ) || ($TMPQUESTION{'qtp'} eq 'lik' )) { ### DED-06 7/17/2002 Replaced: #$quresp = $_[2]; #$qcresp = $TMPQUESTION{'qca'}; #$qcresp =~ s/\n/
/g; ### with the following to print long answers during remediation $qcresp = ""; $quresp = ""; @tmpquresp = split(/\?/, $_[2]); shift @tmpquresp; $keyresponse = $_[3]; @txts = split(/\n/, $TMPQUESTION{'qca'}); @txts_wro = split(/\n/, $TMPQUESTION{'qia'}); foreach $qia (@txts_wro) { push @txts, $qia; } @kans = split(/\?/,$keyresponse); foreach $j (1 .. $#kans) { $jidx = $j-1; @indexs = split(/=/, $kans[$j]); $checked = ($indexs[1] == '1') ? " CHECKED" : ""; $qcresp = join('',$qcresp,"$txts[$indexs[0]]
\n"); $checked = ($tmpquresp[$jidx] eq $jidx) ? " CHECKED" : ""; $quresp = join('',$quresp,"$txts[$indexs[0]]
\n"); } ### END DED-06 ### DED-14 8/9/2002 Added "esa" section below to show multiple answers } elsif ($TMPQUESTION{'qtp'} eq 'esa' ) { $quresp = $_[2]; $qcresp = $TMPQUESTION{'qca'}; $qcresp =~ s/\n/\/g; ### END DED-14 } else { $quresp = $_[2]; $qcresp = $TMPQUESTION{'qca'}; } $textofremediation = "
$ctag $qillus <%=PHRASE.328%> $_[0].\ \;\ \; $qtxt
YOUR ANSWER(S):
$quresp
$qdx
CORRECT ANSWER(S):
$qcresp
"; if ($TMPQUESTION{'qrm'} ne "") { $textofremediation .= " EXPLANATION:
$TMPQUESTION{'qrm'}
"; } $textofremediation .= "
"; return $textofremediation; } ################################################################ # REMEDIATION FIX # if remediated on posting prepare the text of the remediation ################################################################ sub score_question { my ($tsubtest,$tqno,$qcans,$qresp) = @_; #&dbgprint("REMEDIATION FIX:testlib:602 score_question: tsubtest:$tsubtest tqno:$tqno qcans:$qcans qresp:$qresp\n"); my $ansmask = ""; my $iscorrect=0; my ($cans,$trash) = split(/::/, lc($qcans)); my ($crsp,$trash2) = split(/::/, lc($qresp)); $crsp =~ s/\'//; #&dbgprint("REMEDIATION FIX:testlib:614 score_question: cans:$cans crsp:$crsp\n"); if ($cans =~ /[0-9]=[0-1]/ ) { my @ansopts = split(/\?/, $cans); shift @ansopts; for (0 .. $#ansopts) { my $ansdig = ($ansopts[$_] =~ /=1/ ) ? "$_" : "xxx" ; $ansmask = join('?', $ansmask, $ansdig); } #&dbgprint("REMEDIATION FIX:testlib:622 score_question: ansmask:$ansmask crsp:$crsp\n"); $iscorrect = ($ansmask eq $crsp) ? 1 : 0; @ansopts = (); } elsif ($cans =~ /[anorR]\./ ) { my @ansopts = split(/\./, $cans); my $anstype = shift @ansopts; if ($anstype eq 'o') { foreach my $ansopt (@ansopts) { $ansopt++; $ansmask = join('?',$ansmask, $ansopt); } } else { my @albls=&set_answer_labels($anstype); my @cansord=(); for (0 .. $#ansopts) { $cansord[$ansopts[$_]] = $albls[$_]; } foreach my $cansord (@cansord) { $ansmask = join('?', $ansmask, $cansord); } @cansord = (); @albls=(); } #&dbgprint("REMEDIATION FIX:testlib:645 score_question: ansmask:$ansmask crsp:$crsp\n"); $iscorrect = ($ansmask eq $crsp) ? 1 : 0; @ansopts = (); } elsif ($cans =~ m/\;/) { my @ansopts = split(/\;/,$cans); $iscorrect = 0; foreach my $ansopt (@ansopts) { if ($crsp eq $ansopt) { $iscorrect = 1; } } #&dbgprint("REMEDIATION FIX:testlib:656 score_question: ansmask:$ansmask crsp:$crsp\n"); @ansopts = (); } else { #&dbgprint("REMEDIATION FIX:testlib:659 score_question: cans:$cans crsp:$crsp\n"); $iscorrect = ($cans eq $crsp) ? 1 : 0; } return $iscorrect; } ################################################################ # REMEDIATION FIX # if remediated on posting prepare the text of the remediation ################################################################ # ($tsubtest, $tqno) #sub remediate_question { # return ""; #} sub remediate_question { my ($tsubtest,$tqno) = @_; #&dbgprint("REMEDIATION FIX:testlib:675 remediate_question: $tsubtest, $tqno\n"); #&dbgprint("REMEDIATION FIX:testlib:676 remediate_question: REMT:$TEST{'remt'} REMA:$TEST{'rema'}\n"); if (($TEST{'remt'} ne '1') || ($TEST{'rema'} eq '0')) { return ""; } my $remediation=""; my @tqnos = split(/&/, $SUBTEST_QUESTIONS{$tsubtest}); my @qrcans = split(/&/, $SUBTEST_ANSWERS{$tsubtest}); my @qresp = split(/&/, $SUBTEST_RESPONSES{$tsubtest}); my $cans = $qrcans[$tqno]; # = $_[1] ############### my $uresp = $qresp[$tqno]; # = $_[2] ############### my $cflag = &score_question($tsubtest,$tqno,$cans,$uresp); #&dbgprint("REMEDIATION FIX:testlib:691 remediate_question: cflag:$cflag cans:$cans uresp:$uresp\n"); # # IF rema (='3') is remediate on both correct & incorrect answers) # OR uresp is incorrect (='0') and rema (='1') is remediate only on incorrect # OR uresp is correct (='1') and rema (='2') is remediate only on correct # if ( ($TEST{'rema'} eq '3') || (($cflag eq '0') && ($TEST{'rema'} eq '1')) || (($cflag eq '1') && ($TEST{'rema'} eq '2')) ) { %SQUESTION = %QUESTION; &get_question_definition($SUBTEST{'id'}, $SESSION{'clid'}, $tqnos[$tqno]); %TMPQUESTION = %QUESTION; %QUESTION = %SQUESTION; %SQUESTION=(); my ($qrcans, $trash) = split(/::/, $cans); $remediation = ($cflag == 1) ? "

<%=PHRASE.137%>

" : "

<%=PHRASE.343%>

"; my $qremediation = &question_remediation($tqno, $cans, $uresp, $qrcans); $remediation .= $qremediation; my $remfixdbglen=length($remediation); #&dbgprint("REMEDIATION FIX:testlib:708 question_remediation: FREM:$remfixdbglen\n"); %TMPQUESTION=(); $remediation .= "
\">
"; } else { $FORM{'remediated'} = "Y"; } return $remediation; } sub create_test_sequence { $trash = join($pathsep, $testpending, "$_[0].$_[1].$_[2]"); open(TESTFILE, ">$trash") or $msg="failed"; @rows=&package_test_sequence(); foreach $row (@rows) { print TESTFILE "$row\n"; } close TESTFILE; $chmodok = chmod 0666, $trash; ############################################ # addition Backup of registered test #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv # my $coreid = sprintf( "\%d", time); # $trash = join($pathsep, $testpending, "$_[0].$_[1].$_[2].$coreid"); # open(TESTFILE, ">$trash") or $msg="failed"; # foreach $row (@rows) { # print TESTFILE "$row\n"; # } # close TESTFILE; # $chmodok = chmod 0666, $trash; #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # addition Backup of registered test ############################################ } sub put_test_sequence { $trash = join($pathsep, $_[0], "$_[1].$_[2].$_[3]"); open(TESTFILE, ">$trash") or $msg="failed"; @pkg=&package_test_sequence(); foreach $row (@pkg) { print TESTFILE "$row\n"; } close TESTFILE; @pkg=(); } sub package_test_sequence { @rows=(); $hdr = $TEST_SESSION{'clid'}; $hdr = join('&', $hdr, $TEST_SESSION{'uid'}); $hdr = join('&', $hdr, $TEST_SESSION{'tstid'}); $hdr = join('&', $hdr, $TEST_SESSION{'state'}); $hdr = join('&', $hdr, $TEST_SESSION{'dscl'}); $hdr = join('&', $hdr, $TEST_SESSION{'profb'}); $hdr = join('&', $hdr, $TEST_SESSION{'id'}); $hdr = join('&', $hdr, $TEST_SESSION{'profa'}); $hdr = join('&', $hdr, $TEST_SESSION{'srvy'}); $hdr = join('&', $hdr, $TEST_SESSION{'ntfy'}); $hdr = join('&', $hdr, $TEST_SESSION{'emlcnd'}); push @rows, $hdr; $ipts = 1; for $ipts (1 .. 4) { push @rows, $SUBTEST_QUESTIONS{$ipts}; push @rows, $SUBTEST_ANSWERS{$ipts}; push @rows, $SUBTEST_RESPONSES{$ipts}; push @rows, $SUBTEST_SUMMARY{$ipts}; } return @rows; } sub put_question_response { my $questionNo = $_[1]; $qrs = ($FORM{'marked'} ne '') ? "\'" : ""; ### DED 8/9/2002 Separated mcs logic from mcm ### DED 9/2002 Added mca for adaptive if (($QUESTION{'qtp'} eq 'mcs') || ($QUESTION{'qtp'} eq 'mca') || ($QUESTION{'qtp'} eq 'lik')) { @ansc = split(/\n/, $QUESTION{'qca'}); @answ = split(/\n/, $QUESTION{'qia'}); $nanso = $#ansc + $#answ + 1; for $ipqr (0 .. $nanso) { ### DED 8/20/2002 #if (($ipqr != "") && ($ipqr == $FORM{'qrs'})) { if ( (($FORM{'qrs'} != "") || ($FORM{'qrs'} =~ /0/)) && ($ipqr == $FORM{'qrs'}) ) { $qrs = join('?', $qrs, $ipqr); } else { $qrs = join('?', $qrs, "xxx"); } } ### DED 6/28/04 Don't add unanswered to review list #$rdig = $qrs; #$rdig =~ s/xxx//g; #$rdig =~ s/\?//g; #if ($rdig eq '') { $qrs = join('', "\'", $qrs);} } elsif ($QUESTION{'qtp'} eq 'mcm') { @ansc = split(/\n/, $QUESTION{'qca'}); @answ = split(/\n/, $QUESTION{'qia'}); $nanso = $#ansc + $#answ + 1; for $ipqr (0 .. $nanso) { if( $TEST{'seq'} eq 'svy' || ($TEST{'seq'} eq 'dmg' && $TEST{'group'} eq 'Y')) { $rkey = "q$questionNo"; $rkey = join( '-', $rkey, "qrs$ipqr" ); } else { $rkey = "qrs$ipqr"; } $rdig = ($FORM{$rkey} eq '') ? "xxx" : $FORM{$rkey}; ### DED 7/18/2002 #$qrs = join('', $qrs, $rdig); $qrs = join('?', $qrs, $rdig); } ### DED 6/28/04 Don't add unanswered to review list #$rdig = $qrs; #$rdig =~ s/xxx//g; #$rdig =~ s/\?//g; #if ($rdig eq '') { $qrs = join('', "\'", $qrs);} } elsif ($QUESTION{'qtp'} eq 'mtx' || $QUESTION{'qtp'} eq 'mtr') { ($rows, $numrows, $numcols, $cols) = split(/::/, $QUESTION{'qia'}); @rows = split(/\n/, $rows); @cols = split(/\n/, $cols); for $row (0 .. $#rows) { for (0 .. $#cols) { if( $TEST{'seq'} eq 'svy' || ($TEST{'seq'} eq 'dmg' && $TEST{'group'} eq 'Y')) { $rkey = "q$questionNo"; $rkey = join( '-', $rkey, "qrs$row$_" ); } else { $rkey = "qrs$row$_"; } if ($FORM{$rkey} ne '') { $qrs = join('?', $qrs, $FORM{$rkey}); } else { $qrs = join('?', $qrs, "xxx"); } } } ### DED 6/28/04 Don't add unanswered to review list #$resp = $qrs; #$resp =~ s/xxx//g; #$resp =~ s/\?//g; #if ($resp eq '') { $qrs = join('', "\'", $qrs);} #$resp = ''; } elsif (($QUESTION{'qtp'} eq 'mch') || ($QUESTION{'qtp'} eq 'ord')) { @ansc = split(/\n/, $QUESTION{'qca'}); $nanso = $#ansc; for $ipqr (0 .. $nanso) { if( $TEST{'seq'} eq 'svy' ) { $rkey = "q$questionNo"; $rkey = join( '-', $rkey, "qrs$ipqr" ); } else { $rkey = "qrs$ipqr"; } $rdig = ($FORM{$rkey} eq '') ? "xxx" : $FORM{$rkey}; $rdig =~ s/\+/ /g; $rdig =~ s/\&/and/g; ### DED-08 7/17/2002 Replaced #$qrs = join('', $qrs, $rdig); # with $qrs = join('?', $qrs, $rdig); ### END DED-08 } ### DED 6/28/04 Don't add unanswered to review list #if ($rdig =~ /xxx/ ) { $qrs = join('', "\'", $qrs);} } elsif ($QUESTION{'qtp'} eq 'esa' || $QUESTION{'qtp'} eq 'nrt') { ### DED 6/28/04 Don't add unanswered to review list #if ($FORM{'qrs'} eq '') { #$qrs = "\'"; #} else { $qrsu = $FORM{'qrs'}; ### DED-15 8/9/2002 Added line below to strip "+"s $qrsu =~ s/\+/ /g; $qcmt =~ s/\r\n/
/g; $qcmt =~ s/\r/
/g; $qrsu =~ s/\n/
/g; $qrsu =~ s/\&/and/g; #$qrsu = munge($qrsu); $qrs = join('', $qrs, $qrsu); #} } else { $qrs = join('', $qrs, $FORM{'qrs'}); ### DED 6/28/04 Don't add unanswered to review list #if ($qrs eq '') { $qrs = "\'";} } @resps = split(/&/, $SUBTEST_ANSWERS{$_[0]}); $nresps = $#resps; ############################################## # added logging of question response #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv &log_entry($SESSION{'clid'}, $SESSION{'uid'}, "9", "Q\:$questionNo\:$QUESTION{'qid'}\:\:\:\:A\:$resps[$questionNo]\:\:\:\:R\:$qrs"); #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ############################################## @resps = split(/&/, $SUBTEST_RESPONSES{$_[0]}); $rspflds = ""; my $qcmt; for $ipqr (1 .. $nresps) { if ($_[1] eq $ipqr) { $qcmt = $FORM{'qcucmt'}; $qcmt =~ s/\+/ /g ; $qcmt =~ s/\r\n/
/g; $qcmt =~ s/\r/
/g; $qcmt =~ s/\n/
/g; $qcmt =~ s/\&/and/g; $rspflds = join('&', $rspflds, "$qrs\:\:$qcmt"); ############################################## # added logging of question response #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv if ($qcmt ne '') { &log_entry($SESSION{'clid'}, $SESSION{'uid'}, "9", "Q\:$questionNo\:$QUESTION{'qid'}\:\:\:\:C\:$qcmt"); } #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ############################################## } else { $rspflds = join('&', $rspflds, $resps[$ipqr]); } } $SUBTEST_RESPONSES{$_[0]} = $rspflds; $rspflds = ""; @resps = (); @ansc = (); @answ = (); @anso = (); } sub get_previous_response { @resps = split(/&/, $SUBTEST_RESPONSES{$_[0]}); $resp = $resps[$_[1]]; #efl v 12/??/01 #old $resp =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/g; # replace with unmunge $resp = unmunge($resp); #efl ^ ### DED 7/8/04 Moved out to tqrs.pl so marked questions stay marked #$resp =~ s/\'//; @resps = (); return $resp; } sub find_next_marked { @resps = split(/&/, $SUBTEST_RESPONSES{$_[0]}); $nmresps = $#resps; $nsresps = $_[1]; $nsresps = ($_[1] eq $nmresps) ? 1 : $nsresps + 1; if ($nsresps > $nmresps) { $nsresps = 1;} for ($nsresps .. $#resps) { $resp = $resps[$_]; if ($resp =~ /\'/) { return $_; } } $nsresps--; if ($nresps > 0) { for (1 .. $nsresps) { $resp = $resps[$_]; if ($resp =~ /\'/) { return $_; } } } return 0; } sub find_next_unanswered { @resps = split(/&/, $SUBTEST_RESPONSES{$_[0]}); $nmresps = $#resps; $nsresps = $_[1]; $nsresps = ($_[1] eq $nmresps) ? 1 : $nsresps + 1; if ($nsresps > $nmresps) { $nsresps = 1;} for ($nsresps .. $#resps) { ($resp, $trash) = split(/:/, $resps[$_]); $resp =~ s/\'//g; $resp =~ s/\?xxx//g; if ($resp eq '') { return $_; } } $nsresps--; if ($nresps > 0) { for (1 .. $nsresps) { ($resp, $trash) = split(/:/, $resps[$_]); $resp =~ s/\'//g; $resp =~ s/\?xxx//g; if ($resp eq '') { return $_; } } } return 0; } sub find_marked_unanswered { my $marked = ":"; my $unanswered = ":"; my @resps = split(/&/, $SUBTEST_RESPONSES{$_[0]}); for (1 .. $#resps) { ($resp, $trash) = split(/:/, $resps[$_]); $resp =~ s/\?xxx//g; if ($resp =~ /\'/) { $marked .= "$_:"; } $resp =~ s/\'//g; if ($resp eq '') { $unanswered .= "$_:"; } } if ($marked eq ":") { $marked = "" } if ($unanswered eq ":") { $unanswered = "" } return ($marked, $unanswered); } sub build_question_dropdown_list { my ($tsubtest, $marked, $unanswered) = @_; my $questionlist = ""; my @questions=&get_question_list($TEST{'id'}, $SESSION{'clid'}); my %qlist = {}; for (1 .. $#questions) { my $qflds = $questions[$_]; chop ($qflds); my @qdata = split(/&/, $qflds); my ($trash, $qsno) = split(/\./, $qdata[0]); $qlist{$qsno} = substr($qdata[4],0,20); } @qdata = (); my @tquestions = split(/\&/, $SUBTEST_QUESTIONS{$tsubtest}); for (1 .. $#tquestions) { $qind1 = ($marked =~ /:$_:/) ? 'R' : "\ \ "; $qind2 = ($unanswered =~ /:$_:/) ? 'U' : "\ \ "; my ($trash, $qsno) = split(/\./, $tquestions[$_]); $listtext = sprintf("(%u) %20s", $_, $qlist{$qsno}); if ($TEST{'qpv'} eq 'Y' || $qind1 eq 'R' || $qind2 eq 'U') { $questionlist = join('', $questionlist, "\n"); } } @questions = (); @tquestions = (); @qlist = (); return $questionlist; } sub get_question_id { @qids = split(/&/, $SUBTEST_QUESTIONS{$_[0]}); $qid = $qids[$_[1]]; @qids = (); return $qid; } sub prepare_test { my ($clid, $cndid, $authtests, $usetestform, $rmtests) = @_; my $retakeoptions=""; &get_client_profile($clid); my $opts = { restrict_to_availability_window => 0 }; if ($SESSION{'taclid'} eq '') { &get_candidate_profile( $clid, $cndid, $opts); } else { &get_tacl_profile(); } &remove_inprogtest($clid, $cndid, $authtests); #&remove_pending_oldtests($clid, $cndid, $authtests); if ($rmtests ne '') { my @rmtests = split(/\;/, $rmtests); shift @rmtests; foreach (@rmtests) { my $pendfile = join($pathsep, $testpending, "$clid.$cndid.$_"); if (-e $pendfile) { unlink $pendfile; } } } my @atests = &create_newtests_list($clid, $cndid, $authtests); my @testforms = split(/:/, $usetestform); $SYSTEM{'testprepmsg'}=""; $SYSTEM{'testpreperror'}=""; foreach $atest (@atests) { if ($atest ne '') { &get_test_profile($clid, $atest); $TEST_SESSION{'clid'} = $clid; $TEST_SESSION{'uid'} = $cndid; $TEST_SESSION{'tstid'} = $atest; $TEST_SESSION{'state'} = "0.0.0"; $TEST_SESSION{'dscl'} = $TEST{'dscl'}; $TEST_SESSION{'profb'} = $TEST{'profb'}; $TEST_SESSION{'id'} = $TEST{'id'}; $TEST_SESSION{'profa'} = $TEST{'profa'}; $TEST_SESSION{'srvy'} = $TEST{'srvy'}; $TEST_SESSION{'ntfy'} = $TEST{'ntfy'}; $TEST_SESSION{'emlcnd'} = $TEST{'emlcnd'}; @tseqs = ( $TEST{'dscl'}, $TEST{'profb'}, $TEST{'id'}, $TEST{'profa'}, $TEST{'srvy'} ); for $isubtest (1 .. 4) { $SUBTEST_QUESTIONS{$isubtest} = ""; $SUBTEST_ANSWERS{$isubtest} = ""; $SUBTEST_RESPONSES{$isubtest} = ""; $SUBTEST_SUMMARY{$isubtest} = ""; if ($tseqs[$isubtest] ne '') { # DBG &dbgprint("\t$isubtest:$tseqs[$isubtest]\n"); &get_subtest_profile($clid, $tseqs[$isubtest]); # sac - start addition for subject area percentage support # (replaced) $SUBTEST_QUESTIONS{$isubtest} = &build_questions( $clid, $tseqs[$isubtest], $SUBTEST{'noq'}); # (with) &IsTestSBA($clid,$tseqs[$isubtest]); $SYSTEM{'testpreperror'}=""; # DED 6/9/04 handle uploaded test forms if ($isubtest == 2) { $testform = ""; foreach $atestform (@testforms) { if ($atestform eq $tseqs[$isubtest]) { $testform = $atestform; break; } } $SUBTEST_QUESTIONS{$isubtest} = &build_questions( $clid, $tseqs[$isubtest], $SUBTEST{'noq'}, $testform); } else { $SUBTEST_QUESTIONS{$isubtest} = &build_questions( $clid, $tseqs[$isubtest], $SUBTEST{'noq'}); } last if ($SYSTEM{'testpreperror'} ne ""); # sac - end addition for subject area percentage support $SUBTEST_ANSWERS{$isubtest} = &build_answers( $tseqs[$isubtest], $clid, $isubtest, $SUBTEST{'noq'}); @rspflds = split(/&/, $SUBTEST_ANSWERS{$isubtest}); $rspspc = ""; foreach $rspfld (@rspflds) { $rspspc = join('&', $rspspc, ""); } $SUBTEST_RESPONSES{$isubtest} = $rspspc; $rspspc = ""; @rspflds = (); # v sac support for retake options $retakeoptions=$SUBTEST{'slfregenab'}; $retakeoptions=join('.',$retakeoptions,$SUBTEST{'retkcnt'}); $retakeoptions=join('.',$retakeoptions,$SUBTEST{'retkcndtn'}); $retakeoptions=join('.',$retakeoptions,$SUBTEST{'retkwt'}); $retakeoptions=join('.',$retakeoptions,$SUBTEST{'retkwtdly'}); $retakeoptions=join('.',$retakeoptions,$SUBTEST{'retkkeep'}); $retakeoptions=join('.',$retakeoptions,$SUBTEST{'retkautorgstrenab'}); $SUBTEST_QUESTIONS{$isubtest} = join('',$retakeoptions,$SUBTEST_QUESTIONS{$isubtest}); # ^ sac support for retake options } } # sac - start addition for subject area percentage support # (replaced) # &create_test_sequence($clid, $cndid ,$atest); # (with) if ($SYSTEM{'testpreperror'} eq "") { &create_test_sequence($clid, $cndid ,$atest); } else { $SYSTEM{'testprepmsg'}=join('',$SYSTEM{'testprepmsg'},$SYSTEM{'testpreperror'}); $SYSTEM{'testpreperror'}=""; } # sac - end addition for subject area percentage support } } @atests = (); @tseqs = (); } sub build_questions { if ($_[3] ne '') { # DED 6/9/04 use test form $qseq = &build_formqseq($_[1], $_[0]); } elsif (($SUBTEST{'seq'} eq 'std') || ($SUBTEST{'seq'} eq 'svy')) { # sac - start addition for subject area percentage support if ($SUBTEST{'IsTestSBA'}) { $qseq = &build_rndqseq_sba($_[1], $_[0], $_[2]); } else { # sac - end addition for subject area percentage support if ($SUBTEST{'rndq'} eq 'Y') { $qseq = &build_rndqseq($_[1], $_[0], $_[2]); } else { $qseq = &build_stdqseq($_[1], $_[0], $_[2]); } # sac - start addition for subject area percentage support } # sac - end addition for subject area percentage support } elsif ($SUBTEST{'seq'} eq 'dmg') { ### DED 9/11/02 Adaptive Survey (dmg) support $qseq = &build_stdqseq($_[1], $_[0], $_[2]); } return $qseq; } ### wac 072001 - expland labels to 25 from 15, put single quotes around alpha labels. sub set_answer_labels { @albls = (); if ($_[0] eq 'a') { push @albls, ('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y'); } elsif ($_[0] eq 'A') { push @albls, ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y'); } elsif ($_[0] eq 'n') { push @albls, (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25); } elsif ($_[0] eq 'r') { push @albls, (i,ii,iii,iv,v,vi,vii,viii,ix,x,xi,xii,xiii,xiv,xv,xvi,xvii,xviii,xix,xx,xxi,xxii,xxiii,xxiv,xxv); } elsif ($_[0] eq 'R') { push @albls, (I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII,XIII,XIV,XV,XVI,XVII,XVIII,XIX,XX,XXI,XXII,XXIII,XXIV,XXV); } return @albls; } sub get_label_index { @albls = (); if ($_[0] eq 'a') { push @albls, ('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y'); } elsif ($_[0] eq 'A') { push @albls, ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y'); } elsif ($_[0] eq 'n') { push @albls, (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25); } elsif ($_[0] eq 'r') { push @albls, (i,ii,iii,iv,v,vi,vii,viii,ix,x,xi,xii,xiii,xiv,xv,xvi,xvii,xviii,xix,xx,xxi,xxii,xxiii,xxiv,xxv); } elsif ($_[0] eq 'R') { push @albls, (I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII,XIII,XIV,XV,XVI,XVII,XVIII,XIX,XX,XXI,XXII,XXIII,XXIV,XXV); } $retidx = -1; for (0 .. $#albls) { if ($albls[$_] eq $_[1]) { @albls = (); return $_; } } @albls = (); return $retidx; } sub build_answers { $ansrs=""; @qids = split(/&/, $SUBTEST_QUESTIONS{$_[2]}); for $iansno (1 .. $_[3]) { @ansl = (); $ansr=""; $QUESTION{'id'}=$qids[$iansno]; &get_question_definition($_[0], $_[1], $QUESTION{'id'}); if ($QUESTION{'qtp'} eq 'mcm' || $QUESTION{'qtp'} eq 'mcs' || $QUESTION{'qtp'} eq 'mca' || $QUESTION{'qtp'} eq 'lik') { $forcetoend=""; $forcetolast=""; $anidx = 0; $ansmask=""; if ($QUESTION{'qca'} ne '') { $qca = $QUESTION{'qca'}; $qca =~ s/\r/\n/g; $qca =~ s/\n\n/\n/g; @qca = split(/\n/, $qca); foreach $qca (@qca) { if ($qca ne '') { if ($qca =~ /all of/i ) { $forcetoend="$anidx\=1"; } if ($qca =~ /none of/i ) { $forcetolast="$anidx\=1"; } push @ansl, "$anidx\=1"; if ($ansmask eq '') {$ansmask = "<$anidx>";} else {$ansmask = join('', $ansmask, "<$anidx>");} $anidx++; } } @qca = (); } if ($QUESTION{'qia'} ne '') { $qia = $QUESTION{'qia'}; $qia =~ s/\r/\n/g; $qia =~ s/\n\n/\n/g; @qia = split(/\n/, $qia); foreach $qia (@qia) { if ($qia ne '') { if ($qia =~ /all of/i ) { $forcetoend="$anidx\=0"; } if ($qia =~ /none of/i ) { $forcetolast="$anidx\=0"; } push @ansl, "$anidx\=0"; if ($ansmask eq '') {$ansmask = "<$anidx>";} else {$ansmask = join('', $ansmask, "<$anidx>");} $anidx++; } } @qia = (); } $nans = $#ansl + 1; if ($SUBTEST{'rnda'} eq 'Y') { while ($ansmask ne '') { $aidx = int(rand($nans)); if($ansmask =~ /<$aidx>/ && $aidx < $nans) { $ansr = join('?', $ansr, $ansl[$aidx]); $ansmask =~ s/<$aidx>//g; } } } else { for (0 .. $#ansl) { $ansr = join('?', $ansr, $ansl[$_]); } } if ($forcetoend ne '') { $ansr =~ s/\?$forcetoend//g; $ansr = join('?', $ansr, "$forcetoend"); } if ($forcetolast ne '') { $ansr =~ s/\?$forcetolast//g; $ansr = join('?', $ansr, "$forcetolast"); } } elsif ($QUESTION{'qtp'} eq 'mtx' || $QUESTION{'qtp'} eq 'mtr') { if ($QUESTION{'qca'} ne '') { $qca = $QUESTION{'qca'}; $qca =~ s/\r/\n/g; $qca =~ s/\n\n/\n/g; @qca = split(/\n/, $qca); foreach $qca (@qca) { $ansr = join('?', $ansr, $qca); } @qca = (); } } elsif ($QUESTION{'qtp'} eq 'mch') { $anidx = 0; $ansmask=""; if ($QUESTION{'qia'} ne '') { $qia = $QUESTION{'qia'}; $qia =~ s/\r/\n/g; $qia =~ s/\n\n/\n/g; @qia = split(/\n/, $qia); foreach (0 .. $#qia) { $qia = $qia[$_]; if ($qia ne '') { push @ansl, "$anidx"; if ($anidx == 0) {$ansmask = "<$anidx>";} else {$ansmask = join('', $ansmask, "<$anidx>");} $anidx++; } } @qia = (); } $nans = $#ansl+1; ### ADT-01 9/02/2001 prevent right half of matching questions from scrambling if( $TEST{'rnda'} eq 'Y' ) { ### END ADT-01 change affects surveys only $ansr=$QUESTION{'qalb'}; while ($ansmask ne '') { $aidx = int(rand($nans)); if($ansmask =~ /<$aidx>/ && $aidx < $nans) { $ansr = join('.', $ansr, $ansl[$aidx]); $ansmask =~ s/<$aidx>//g; } } ### ADT-02 9/02/2001 } else { ### DED-01 7/16/2002 Added line below to include ### label in answer ("a","n", or "r") $ansr=$QUESTION{'qalb'}; for( 0 .. $#ansl ) { $ansr = join( '.', $ansr, $ansl[$_] ); } } ### END ADT-02 9/02/2001 } elsif ($QUESTION{'qtp'} eq 'ord') { $anidx = 0; $ansmask=""; if ($QUESTION{'qca'} ne '') { $qca = $QUESTION{'qca'}; $qca =~ s/\r/\n/g; $qca =~ s/\n\n/\n/g; @qca = split(/\n/, $qca); foreach $qca (@qca) { if ($qca ne '') { push @ansl, "$anidx"; if ($anidx == 0) {$ansmask = "<$anidx>";} else {$ansmask = join('', $ansmask, "<$anidx>");} $anidx++; } } @qca = (); } $nans = $#ansl+1; ###wac v if( $SUBTEST{'rnda'} eq 'Y' ) { ###wac ^ ### DED-02 7/16/2002 Replaced # $ansr="o"; ### with $ansr=$QUESTION{'qalb'}; ### to place label in answer ("a","n", or "r") while ($ansmask ne '') { $aidx = int(rand($nans)); if($ansmask =~ /<$aidx>/ && $aidx < $nans) { $ansr = join('.', $ansr, $ansl[$aidx]); $ansmask =~ s/<$aidx>//g; } } ###wac v } else { ### DED-03 7/16/2002 Added line below to include ### label in answer ("a","n", or "r") $ansr=$QUESTION{'qalb'}; for( 0 .. $#ansl ) { $ansr = join( '.', $ansr, $ansl[$_] ); } } # } ###wac ^ } elsif ($QUESTION{'qtp'} eq 'nrt') { $ansr = ""; ### sac v multianswer esa support } elsif ($QUESTION{'qtp'} eq 'esa') { $ansr = $QUESTION{'qca'}; $ansr =~ s/\r/\n/g; $ansr =~ s/\n\n/\n/g; $ansr =~ s/\n/\;/g; ### sac ^ multianswer esa support } else{ $ansr = $QUESTION{'qca'}; } $scwght = ($QUESTION{'wght'} eq '') ? '1' : $QUESTION{'wght'}; $scpts = ($QUESTION{'pts'} eq '') ? '1' : $QUESTION{'pts'}; $scded = ($QUESTION{'ded'} eq '') ? '0' : $QUESTION{'ded'}; $scoring = join(':', $QUESTION{'subj'}, $scwght, $scpts, $scded); $ansr = join('::', $ansr, $scoring); $ansrs = join('&', $ansrs, $ansr); } @ansl = (); @qids = (); return $ansrs; } sub build_question_pool { @qtpool = (); @qcountrecs = &get_question_list($_[0],$_[1]); @qcountflds = split(/&/, $qcountrecs[0]); push @qtpool, $qcountrecs[0]; for (1 .. $#qcountflds) { $qcountfldidx = $_; last if($qcountflds[$_] eq 'qil'); } for (1 .. $#qcountrecs) { @qcountflds = split(/&/, $qcountrecs[$_]); if ($qcountflds[$qcountfldidx] ne 'Y') { push @qtpool, $qcountrecs[$_]; } } @qcountrecs = (); @qcountflds = (); return @qtpool; } sub build_rndqseq{ #print STDERR "RNDQSEQ\n"; # randomize @qpool = &build_question_pool($_[0],$_[1]); $qrec=""; $nqpool = $#qpool; $qlimit = ($nqpool > $_[2]) ? $_[2] : $nqpool; for $i (1 .. $qlimit) { $qrec = join('&', $qrec, "<$i>"); } for $ibrs (1 .. $qlimit) { $qidx = int(rand($#qpool)); $qidx++; ($qid,$trash) = split(/&/, $qpool[$qidx]); $qrec =~ s/<$ibrs>/$qid/g; if ($qidx == $#qpool) { pop(@qpool); } else { $qpool[$qidx] = pop(@qpool); } } @qpool = (); return $qrec; } sub build_stdqseq { @qpool = &build_question_pool($_[0],$_[1]); $qrec=""; $nqpool = $#qpool; $qlimit = ($nqpool > $_[2]) ? $_[2] : $nqpool; for $ibrs (1 .. $qlimit) { ($qid,$trash) = split(/&/, $qpool[$ibrs]); $qrec = join('&', $qrec, $qid); } @qpool = (); return $qrec; } sub build_formqseq { ### DED 6/11/04 build test from form file ### may later add formid as 3rd parameter to pick which form my ($test, $clid) = @_; open(FORMFILE, "<$questionroot/$test.$clid.form") or die "Can't open $questionroot/$test.$clid.form\n"; my @forminfo = ; close(FORMFILE); shift @forminfo; ### DED 6/11/04 for now only use first form in file my ($formid, $quesnos) = split(/\&/, $forminfo[0]); my @quesnos = split(/,/,$quesnos); $qrec=""; foreach $ques (@quesnos) { $qid = sprintf("%s.%03u", $test, $ques); $qrec = join('&', $qrec, $qid); } return $qrec; } sub admin_testresults { my $registrar = $_[1]; my $adminbody = ""; if ((!$registrar && $TEST{'emlesaopt'} eq 'Y' && $TEST{'emlesahtmlopt'} eq 'Y') || ($registrar && $TEST{'emlesaropt'} eq 'H')) { # Prepare HTML attachment $trtime = $mmtime; $trtime =~ s/ /_/g; my $html = `./testreport.pl $FORM{'tid'} $SESSION{'clid'} $SESSION{'uid'} $TEST{'id'} $trtime "$results[0]" $results[1] $TEST{'noq'} $results[2]`; $htmlfile = "$SESSION{'clid'}.$SESSION{'uid'}.$TEST{'id'}.htm"; $adminbody = "${mm_encoded_html}\n" ; $adminbody .= encode_base64($html) ; } else { @testqs = &get_question_list($TEST{'id'}, $SESSION{'clid'}); $mmflds = $testqs[0]; chop($mmflds); $mmidx = 0; @mmflds = split(/&/, $mmflds); for (0 .. $#mmflds) { $mmidx = ($mmflds[$_] eq 'qtx') ? $_ : 0; last if ($mmidx != 0); } @mmflds = (); for (1 .. $#testqs) { $testqs = $testqs[$_]; chop ($testqs); ($mmqid, $trash) = split(/&/, $testqs); $MMQUESTION{$mmqid} = $testqs; } @testqs = (); @mmqs = split(/&/, $SUBTEST_QUESTIONS{$_[0]}); @mmas = split(/&/, $SUBTEST_RESPONSES{$_[0]}); $mmfullbody = "${mm_7bit_test}\n\nRESPONSES:\n\n"; for ( 1.. $#mmas) { $testqs = $MMQUESTION{$mmqs[$_]}; @mmflds = split(/&/, $testqs); $mmfullbody = join('', $mmfullbody, "$_:$mmqs[$_]\n"); $mmfullbody = join('', $mmfullbody, "Q: $mmflds[$mmidx]\n"); $qqans = $mmas[$_]; $qqans = unmunge($qqans); $mmfullbody = join('', $mmfullbody, "R: $qqans\n\n"); } @mmflds = (); @mmqs = (); @mmas = (); $mmfullbody =~ s/xxx/yyy/g; $adminbody = join('', $adminbody, $mmfullbody); } return $adminbody; } sub send_testresults { $logfile = "$SESSION{'clid'}.$SESSION{'uid'}"; @loglines = get_log($logfile); @results = split(/&/, $SUBTEST_SUMMARY{$_[0]}); @startlines = grep( /Test Start/,@loglines); ($starttime, $startsession, $startnum, $startmsg) = split(/,/,@startlines[$#startlines]); if ($_[1]) { $mmtime = $_[1]; } else { $mmtime = &format_date_time("dd-mmm-yyyy hh:nn:ss GMT", 1, "0"); } if ($_[2]) { $mmdate = $_[2]; $user_only = 1; } else { $mmdate = &format_date_time("dd-mmm-yyyy", 1, "0"); $user_only = 0; } $mmfrom = $CLIENT{'email_from'}; ### Compute score if ($TEST{'scr'} eq '3') { $mmscore = "***** Not Scored *****\n\n"; } ##wac v 01/03/02 change wording if scoring is by cummulative points elsif ($SUBTEST{'scr'} eq '2') { $minpass = ($SUBTEST{'minpass'} eq "") ? "Not Specified" : $SUBTEST{'minpass'}." \points"; $mmscore = " Points from Correct Answers: $results[0] Points deducted for Incorrect Answers: $results[1] Total Number of Questions: $TEST{'noq'} Cummulative Score: $results[2] \points Passing Score: $minpass "; ##wac v 01/08/02 change wording if scoring is by weighted percentage } elsif ($SUBTEST{'scr'} eq '1') { $minpass = ($SUBTEST{'minpass'} eq "") ? "Not Specified" : $SUBTEST{'minpass'}." \%"; $mmscore = " Points (total) for Correct Answers: $results[0] Points (total) for Incorrect Answers: $results[1] Total Number of Questions: $TEST{'noq'} Score: $results[2] \% Passing Score: $minpass "; } else { $minpass = ($SUBTEST{'minpass'} eq "") ? "Not Specified" : $SUBTEST{'minpass'}." \%"; $mmscore = " Correct Answers: $results[0] Incorrect Answers: $results[1] Total Number of Questions: $TEST{'noq'} Score: $results[2] \% Passing Score: $minpass "; } # Compute the email boundary string used to divide multi-part # email messages. my $myrand ; my $rand_str ; my $boundary_str = "Acts-Corp-Boundary-" ; foreach $i (1..5) { $myrand = rand ; $rand_str = sprintf "%12.12f", $myrand ; $rand_str =~ s/^0\.// ; $boundary_str .= $rand_str ; } $MIME_start = "MIME-version: 1.0\n" ; $MIME_start .= "Content-Type: multipart/mixed; boundary=" ; $MIME_start .= "\"${boundary_str}\"\n\n" ; $mm_7bit_text = "\n--${boundary_str}\n" ; $mm_7bit_text .= "Content-type: text/plain\n" ; $mm_7bit_text .= "Content-transfer-encoding: 7bit\n" ; $mm_encoded_html = "\n--${boundary_str}\n" ; $mm_encoded_html .= "Content-type: text/html\n" ; $mm_encoded_html .= "Content-transfer-encoding: base64\n" ; if (!$user_only) { ### Send results to admin notification list $mmto = $TEST{'ntfy'}; $mmsubj = "Completed: $CANDIDATE{'nmf'} $CANDIDATE{'nml'}"; $mmnoreply = "DO NOT REPLY TO THIS MESSAGE"; $mmheader = " Date: $mmdate Site: $TEST_SESSION{'clid'} Candidate: $SESSION{'uid'} Name: $CANDIDATE{'nmf'} $CANDIDATE{'nml'} Description: $TEST{'desc'} - $TEST{'id'} Start Time: $starttime Compl Time: $mmtime "; $mmbody = join('', ${MIME_start}, ${mm_7bit_text}, $mmnoreply, $mmheader, $mmscore); ### Send notification to admin distribution list if ($mmto ne '') { my $adminbody = $mmbody; if ($TEST{'emlesaopt'} eq 'Y') { my $admin_testresults = &admin_testresults($_[0],0); $adminbody = join('', $adminbody, $admin_testresults); $mmorder = "\n$SUBTEST_ANSWERS{$_[0]}\n"; $adminbody = join('', $adminbody, ${mm_7bit_text}, $mmorder); } &send_mail($mmfrom, $mmto, $mmsubj, $adminbody); } # HBI Defect - %CLIENT is not populated, So $CLIENT{'clid'} is empty. # HBI Defect - The code should call get_client_profile($TEST_SESSION{'clid'}) to populate %CLIENT. # HBI Defect - get_client_profile is in cybertestlib.pl. ### Is there a registrar? if ($TEST{'emlesaropt'} ne 'N' && &get_a_key("cnd.$CLIENT{'clid'}", $CANDIDATE{'createdby'}, "registrar") eq 'Y') { ### Does registrar have an e-mail address? $mmto = &get_a_key("cnd.$CLIENT{'clid'}", $CANDIDATE{'createdby'}, "eml"); if ($mmto ne '') { my $notifbody = $mmbody; ### Send notification to registrar if ($TEST{'emlesaropt'} eq 'H') { my $admin_testresults = &admin_testresults($_[0],1); $notifbody = join('', $MIME_start, $mm_7bit_text, $notifbody); $notifbody .= join('', $admin_testresults); $mmorder = "\n$SUBTEST_ANSWERS{$_[0]}\n"; $notifbody .= join('', $mm_7bit_text, $mmorder); } &send_mail($mmfrom, $mmto, $mmsubj, $notifbody); } } } # END if (!$user_only) if ($TEST{'emlcndopt'} eq 'Y') { ### Send results to candidate $mmto = $CANDIDATE{'eml'}; $mmsubj = "Final results - $CANDIDATE{'nmf'} $CANDIDATE{'nml'}"; $mmbody = "DO NOT REPLY TO THIS MESSAGE Date: $mmdate Site: $TEST_SESSION{'clid'} Candidate: $CANDIDATE{'uid'} Name: $CANDIDATE{'nmf'} $CANDIDATE{'nml'} Description: $TEST{'desc'} - $TEST{'id'} The test administrator has been notified of your test scores as shown below. Candidate completed the item above at $mmtime on the specified date with the following results. $mmscore "; ##wac 01/03/02 - added one line above - $mmscore. Idea is to pick up wording for results from above, should still be in $mmscore #Correct Answers: $results[0] #Incorrect Answers: $results[1] #Total Number of Questions: $TEST{'noq'} #Score: $results[2] \%\n"; ##wac ^ if ($mmto ne '') { &send_mail($mmfrom, $mmto, $mmsubj, $mmbody); } } } sub send_start_notification { $mmdate = &format_date_time("dd-mmm-yyyy", "1", "0"); $mmtime = &format_date_time("hh:nn:ss GMT", "1", "0"); $mmfrom = $CLIENT{'email_from'}; if ($_[0] ne '') { $mmto = $_[0]; } else { $mmto = $TEST{'ntfy'}; } $mmsubj = "Activity Initiated: $CANDIDATE{'nmf'} $CANDIDATE{'nml'}"; $mmbody = "DO NOT REPLY TO THIS MESSAGE Date: $mmdate Candidate: $SESSION{'uid'} Site: $TEST_SESSION{'clid'} Name: $CANDIDATE{'nmf'} $CANDIDATE{'nml'} Description: $TEST{'desc'} - $TEST{'id'} Candidate has started the above item at $mmtime on this date. "; &send_mail($mmfrom, $mmto, $mmsubj, $mmbody); } sub send_resume_notification { $mmdate = &format_date_time("dd-mmm-yyyy", "1", "0"); $mmtime = &format_date_time("hh:nn:ss GMT", "1", "0"); $mmfrom = $CLIENT{'email_from'}; if ($_[0] ne '') { $mmto = $_[0]; } else { $mmto = $TEST{'ntfy'}; } $mmsubj = "Activity Resumed: $CANDIDATE{'nmf'} $CANDIDATE{'nml'}"; $mmbody = "DO NOT REPLY TO THIS MESSAGE Date: $mmdate Candidate: $SESSION{'uid'} Site: $TEST_SESSION{'clid'} Name: $CANDIDATE{'nmf'} $CANDIDATE{'nml'} Description: $TEST{'desc'} - $TEST{'id'} Candidate has resumed the above item at $mmtime on this date. "; &send_mail($mmfrom, $mmto, $mmsubj, $mmbody); } sub send_pause_notification { $mmdate = &format_date_time("dd-mmm-yyyy", "1", "0"); $mmtime = &format_date_time("hh:nn:ss GMT", "1", "0"); $mmfrom = $CLIENT{'email_from'}; if ($_[0] ne '') { $mmto = $_[0]; } else { $mmto = $TEST{'ntfy'}; } if ($TEST{'seq'} eq 'svy' || $TEST{'seq'} eq 'dmg') { $itemdescription = "Survey"; } else { $itemdescription = "Test"; } $mmsubj = "$itemdescription Paused: $CANDIDATE{'nmf'} $CANDIDATE{'nml'}"; $mmbody = "DO NOT REPLY TO THIS MESSAGE Date: $mmdate Site: $TEST_SESSION{'clid'} Candidate: $SESSION{'uid'} Name: $CANDIDATE{'nmf'} $CANDIDATE{'nml'} Description: $TEST{'desc'} - $TEST{'id'} Candidate Paused the above item at $mmtime on this date. "; &send_mail($mmfrom, $mmto, $mmsubj, $mmbody); } sub send_declined_notification { $mmdate = &format_date_time("dd-mmm-yyyy", "1", "0"); $mmtime = &format_date_time("hh:nn:ss GMT", "1", "0"); $mmfrom = $CLIENT{'email_from'}; $mmto = $TEST{'ntfy'}; $mmsubj = "CONFIDENTIALITY DECLINED: $CANDIDATE{'nmf'} $CANDIDATE{'nml'}"; $mmbody = "DO NOT REPLY TO THIS MESSAGE Date: $mmdate Site: $TEST_SESSION{'clid'} Candidate: $SESSION{'uid'} Name: $CANDIDATE{'nmf'} $CANDIDATE{'nml'} Description: $TEST{'desc'} - $TEST{'id'} Candidate declined the confidentality agreement at $mmtime on this date. The item above was terminated and unregistered. "; # The following email has all text, and should not need multipart lines. &send_mail($mmfrom, $mmto, $mmsubj, $mmbody); } # # &show_test_worksheets($TEST_SESSION{'clid'}, $TEST_SESSION{'id'}) # sub show_test_worksheets { @pagenos = split(/\./, $TEST{'Ins'}); $pagecount = $#pagenos + 1; # show test instructions $jvars = ""; $jscript = "i=1\;\nimax=$pagecount\;\n\n"; $buttons = ""; $jointer=""; for (0 .. $#pagenos) { $x = int($_) + 1; $fpath = join($pathsep,$questionroot,"Ins","$TEST{'id'}.$SESSION{'clid'}.$pagenos[$_]"); $wsURL = "$cgiroot/twsprint.pl?tid=$SESSION{'tid'}\&fn=$fpath"; $jvars = join($jointer, $jvars, "wdw$x,sWorksheet$x"); $jscript = join('', $jscript, "sWorksheet$x=\"$wsURL\"\;\n"); $tmptitle = &get_test_worksheet($TEST{'id'},$SESSION{'clid'},$pagenos[$_]); @tmphtml = split(//, $tmptitle); $tmptitle = $tmphtml[1]; @tmphtml = split(/<\/TITLE>/, $tmptitle); $tmptitle = ($tmphtml[0] eq '') ? "Worksheet $x" : "$tmphtml[0]"; if ($SESSION{'browserapp'} eq 'MSIE') { $buttons = join('', $buttons, "<INPUT TYPE=BUTTON VALUE=\"Reprint $tmptitle\" onClick=\"return Reprintpage($x)\"><BR>\n"); } else { $buttons = join('', $buttons, "<INPUT TYPE=BUTTON VALUE=\"Print $tmptitle\" onClick=\"return Reprintpage($x)\"><BR>\n"); } $jointer=","; } $FORM{'jscript'} = join('', "var $jvars\;\n", $jscript); $FORM{'buttons'} = $buttons; &show_template("qins"); } # sac - start addition for subject area percentage support sub IsTestSBA { my ($clid, $tstid) = @_; my $said; my $skid; my $saskcount; my $fn = join( $pathsep, $questionroot, "$tstid.$clid.sba.mtx"); my $bOK=0; $SUBTEST{'IsTestSBA'}=0; if (open(TMPFILE, "<$fn")) { my @sbarecs = <TMPFILE>; close TMPFILE; if ($#sbarecs == 2) { chop $sbarecs[0]; chop $sbarecs[1]; chop $sbarecs[2]; my @samtxrecs=split(/\,/,$sbarecs[2]); if ($#samtxrecs != -1) { ($said,$skid,$saskcount) = split(/\:/,$samtxrecs[0]); if (($said ne '') && ($skid ne '') && ($saskcount ne '')) { $SUBTEST{'IsTestSBA'} = -1; $SUBTEST{'sbausesubj'} = $sbarecs[0]; $SUBTEST{'sbauseskill'} = $sbarecs[1]; $SUBTEST{'sbamtx'} = $sbarecs[2]; $bOK=-1; } } } } return $bOK; } sub build_rndqseq_sba { #print STDERR "RNDQSEQ_SBA\n"; my ($tstid, $clid, $tnoq) = @_; # randomize my $i; my $j; my $qrec=""; my $qrecall=""; my $nqpool=0; my $qlimit=0; my @flds; my $ibrs; my $qidx; my $trash; my $sasksubj; my $saskskill; my $saskcount; my $saskqtotal=0; my @qpool = (); my $sgrepfor=""; my $nm; my $pct; my $rnd; my $fixord; my %sarnds; my %safxos; my %sapools; # Debug ANALYSIS #if ($SUBTEST{'rndq'} eq "Y") { print STDERR "on\n"; } else { print STDERR "off\n";} # reset the error indicator $SYSTEM{'testpreperror'}=""; # get the question list excluding obsolete questions my @qpoolmaster = &build_question_pool($tstid,$clid); @flds = split(/&/, $qpoolmaster[0]); for $i (0 ..$#flds) { if ($flds[$i] eq 'subj') { $j=$i; $qpoolmaster[0] = join('&', $flds[0], "$flds[$j]"); } } for $i (1 ..$#qpoolmaster) { @flds = split(/&/, $qpoolmaster[$i]); if ($flds[$j] =~ /\./ ) { $qpoolmaster[$i] = join('&', $flds[0], "$flds[$j]"); } else { $qpoolmaster[$i] = join('&', $flds[0], "$flds[$j].0"); } } # # build subject area parameters array # %sarnds randomization flags # %safxos fixed orders # %sapools accumulated questions for all subj skill levels # my @saparms=split(/\,/,$SUBTEST{'sbausesubj'}); for $i (0 .. $#saparms) { if ($saparms[$i] ne '') { ($nm,$pct,$rnd,$fixord) = split(/:/, $saparms[$i]); $sarnds{$nm}=int($rnd); $safxos{$nm}=int($fixord); $sapools{$nm}=""; } } @saparms=(); # build subject skill array from $SUBTEST{'sbamtx'} my @sasks = split(/\,/, $SUBTEST{'sbamtx'}); $j=$#sasks+1; # for subject area create the question pools # and name the index in qspool array my @qspool; $j=-1; for $i (0 .. $#sasks) { ($sasksubj,$saskskill,$saskcount) = split(/\:/,$sasks[$i]); if (($sasksubj eq '') || ($saskskill eq '') || ($saskcount eq '')) { # file format error $SYSTEM{'testpreperror'}="Unable to prepare $tstid: subject area skill level matrix format error."; } else { # prepare the question pool for the subject area my $sklvlid=($saskskill eq 'BASIC') ? "0" : ""; $sklvlid=($saskskill eq 'INTERMEDIATE') ? "1" : $sklvlid; $sklvlid=($saskskill eq 'ADVANCED') ? "2" : $sklvlid; # $sgrepfor=join('.',"\&$sasksubj","$sklvlid"); @qpool = grep( /$sgrepfor/,@qpoolmaster); # # prepare the sequential or randomized question list # and merge all skill levels for each subject area # $qrec=""; unshift @qpool,$qrec; $nqpool = $#qpool; if ($nqpool >= $saskcount) { if (($sarnds{$sasksubj} == 1) || ($SUBTEST{'rndq'} eq 'Y')) { $qrec=&randomize_qpool($saskcount,@qpool); } else { $qrec=&sequential_qpool($saskcount,@qpool); } $sapools{$sasksubj}=join('', $sapools{$sasksubj}, $qrec); if (($qspool[$j] ne $sasksubj) || ($j == -1)) { $j++; $qspool[$j]=$sasksubj; } } else { # Insufficient question count to meet required distribution $SYSTEM{'testpreperror'}="Unable to prepare $tstid:<br>Insufficient number of $sasksubj.$saskskill questions in the pool.<br>$saskcount required : $nqpool defined and active.<br>"; } @qpool = (); } } @sasks=(); @qpoolmaster=(); if ($SYSTEM{'testpreperror'} eq '') { # # if there were no errors # if ($SUBTEST{'rndq'} eq 'Y') { # # if globally randomized combine the pools and randomize # $qrec=""; for $i (0 .. $#qspool) { $sasksubj=$qspool[$i]; $qrec=join('',$qrec,$sapools{$sasksubj}); } @qpool=split(/&/,$qrec); $saskcount=$#qpool; $qrecall=&randomize_qpool($saskcount,@qpool); @qpool=(); } else { # # keep subject area together and randomization within # for $i (0 .. $#qspool) { $sasksubj=$qspool[$i]; if ($sarnds{$sasksubj} == 0) { @qpool=split(/&/,$sapools{$sasksubj}); $saskcount=$#qpool; @qpool=(); } else { @qpool=split(/&/,$sapools{$sasksubj}); $saskcount=$#qpool; $sapools{$sasksubj}=&randomize_qpool($saskcount,@qpool); @qpool=(); } } # # check for fixed order and randomize the others # my @sbjfixed=(); my @sbjrndmz=(); for $i (0 .. $#qspool) { $sasksubj=$qspool[$i]; if ($safxos{$sasksubj} == 0) { push @sbjrndmz, $sasksubj; } else { $qrec="$safxos{$sasksubj}.$sasksubj"; push @sbjfixed,$qrec; } } if ($#sbjrndmz != -1) { $qrec=""; unshift @sbjrndmz, $qrec; $saskcount=$#sbjrndmz; $qrec=&randomize_qpool($saskcount,@sbjrndmz); @sbjrndmz=split(/&/,$qrec); $qrec=shift @sbjrndmz; } if ($#sbjfixed != -1) { @qpool = sort @sbjfixed; @sbjfixed = @qpool; @qpool = (); } #for $i (0 .. $#sbjrndmz) { #} #for $i (0 .. $#sbjfixed) { #} @qspool=(); if (($#sbjrndmz != -1) && ($#sbjfixed != -1)) { $saskcount=$#sbjrndmz + $#sbjfixed + 2; $qrec=shift @sbjfixed; ($j,$sasksubj) = split(/\./,$qrec); for $i ( 1 .. $saskcount) { if( $i == $j) { push @qspool, $sasksubj; if ($#sbjfixed == -1) { $sasksubj=""; $j=0; } else { $qrec=shift @sbjfixed; ($j,$sasksubj) = split(/\./,$qrec); } } else { if ($#sbjrndmz != -1) { $qrec=shift @sbjrndmz; push @qspool, $qrec; } } } if ($sasksubj ne '') { push @qspool, $sasksubj; $j=1000; while (($#sbjfixed != -1) && ($j > 0)) { $qrec=shift @sbjfixed; ($j,$sasksubj) = split(/\./,$qrec); push @qspool, $sasksubj; $j--; } } #for $i (0 .. $#qspool) { #} } else { ### DED 11/02/2002 Changed #if ($#sbjfixed != -1) { ### to if ($#sbjrndmz != -1) { @qspool = @sbjrndmz; } else { @qspool = @sbjfixed; } } @sbjfixed=(); @sbjrndmz=(); $qrecall=""; for $i (0 .. $#qspool) { $sasksubj = $qspool[$i]; $qrecall=join('',$qrecall,$sapools{$sasksubj}); } } } @qspool=(); %sarnds={}; %safxos={}; %sapools={}; # ANALYSIS #@qpool=split(/&/,$qrecall); #@qpool=(); return $qrecall; } sub randomize_qpool { my ($qlmt,@qp) = @_; my $i; my $j; my $nqp; my $qid; my $trash; my $ibrs; my $qidx; my $qrec=""; for $i (1 .. $qlmt) { $qrec = join('&', $qrec, "<$i>"); } for $ibrs (1 .. $qlmt) { $qidx = int(rand($#qp)); $qidx++; ($qid,$trash) = split(/&/, $qp[$qidx]); $qrec =~ s/<$ibrs>/$qid/g; if ($qidx == $#qp) { pop(@qp); } else { $qp[$qidx] = pop(@qp); } } return $qrec; } sub sequential_qpool { my ($qlmt,@qp) = @_; my $i; my $qid; my $trash; my $qrec=""; for $i (1 .. $qlmt) { ($qid,$trash) = split(/&/, $qp[$i]); $qrec = join('&', $qrec, $qid); } return $qrec; } # sac - end addition for subject area percentage support # v sac anonymous submission support sub make_anonymous { # test completed, terminated, or time expired # split off the anonymous parts if permitted and requested &get_test_sequence( $SESSION{'clid'}, $SESSION{'uid'}, $FORM{'tstid'}, $testcomplete); my $cnt = unlink "$testcomplete/".$SESSION{'clid'}.".".$SESSION{'uid'}.".".$FORM{'tstid'}; my @anonymityFlags = split(/\;/,$SESSION{'anonymity'}); my $anonflag; my $subtstno; my $subtstnm; my $subtstanon; my $tstfile; my $tsthistfile; my $historyopen=0; my $flsep="\<\<\>\>"; my $clid=$TEST_SESSION{'clid'}; my @tests=(); $tests[1]=$TEST_SESSION{'profb'}; $tests[2]=$TEST_SESSION{'id'}; $tests[3]=$TEST_SESSION{'profa'}; $tests[4]=$TEST_SESSION{'srvy'}; my @flags=split(/\,/,",N,N,N,N"); foreach $anonflag (@anonymityFlags) { ($subtstno,$subtstnm,$subtstanon) = split(/\./,$anonflag); $flags[$subtstno]=$subtstanon; } my $ident; my $uid; my $chmodOK; my $dscl=$TEST_SESSION{'dscl'}; for $i (1 .. 4) { if ($tests[$i] ne "") { if ($flags[$i] eq 'Y') { $uid=&get_anon_seqno($clid,$tests[$i]); } else { $uid=$TEST_SESSION{'uid'}; } $tstfile=join($pathsep, $testcomplete, "$clid.$uid.$tests[$i]"); $ident=$clid; $ident=join('&',$ident,$uid); $ident=join('&',$ident,$tests[$i]); $ident=join('&',$ident,$TEST_SESSION{'state'}); if ($i == 2) { $ident=join('&',$ident,$dscl); } else { $ident=join('&',$ident,""); } $ident=join('&',$ident,""); $ident=join('&',$ident,$tests[$i]); $ident=join('&',$ident,""); $ident=join('&',$ident,""); $ident=join('&',$ident,$TEST_SESSION{'ntfy'}); $ident=join('&',$ident,$TEST_SESSION{'emlcnd'}); if (open(TOFILE, ">$tstfile")) { print TOFILE "$ident\n"; print TOFILE "\n\n\n\n"; print TOFILE "$SUBTEST_QUESTIONS{$i}\n"; print TOFILE "$SUBTEST_ANSWERS{$i}\n"; print TOFILE "$SUBTEST_RESPONSES{$i}\n"; print TOFILE "$SUBTEST_SUMMARY{$i}\n"; print TOFILE "\n\n\n\n"; print TOFILE "\n\n\n\n"; close TOFILE; $chmodOK = chmod 0666,$tstfile; } $logfile = "$SESSION{'clid'}.$SESSION{'uid'}"; # DED 1/03/04 no longer puting starttime in history file #@loglines = get_log($logfile); #@startlines = grep( /Test Start/,@loglines); #($starttime, $startsession, $startnum, $startmsg) = split(/,/,@startlines[$#startlines]); $tsthistfile=join($pathsep, $testcomplete, "$clid.$tests[$i].history"); if (open(TOFILE, ">>$tsthistfile")) { $historyopen=1; } else { if (open(TOFILE, ">$tsthistfile")) { $historyopen=1; } else { $historyopen=0; } } if ($historyopen) { print TOFILE "$endtime$flsep"; print TOFILE "$ident$flsep"; print TOFILE "$SUBTEST_QUESTIONS{$i}$flsep"; print TOFILE "$SUBTEST_ANSWERS{$i}$flsep"; print TOFILE "$SUBTEST_RESPONSES{$i}$flsep"; print TOFILE "$SUBTEST_SUMMARY{$i}\n"; } close TOFILE; $chmodOK = chmod 0666,$tsthistfile; } } } sub get_anon_seqno { my ($clid,$testid) = @_; my $sgrepfor; my $entry; my $cnt; my $iter; my $uid; my @dots=(); my @dots2=(); my @entries=(); my @segs=(); my $nxtid="anon"; my @clsegs=split(/\./,$clid); my $tstclid=""; opendir(DIR, $testcomplete); @dots = readdir(DIR); closedir DIR; opendir(DIR, $testinprog); @dots2 = readdir(DIR); closedir DIR; push @dots, @dots2; $cnt=0; if ($#dots != -1) { $sgrepfor=join('.',"$clid","anon","\*","$testid"); @entries = grep( /$sgrepfor/,@dots); @dots = (); $cnt=0; foreach $entry (@entries) { @segs=split(/\./, $entry); $tstclid=""; for (0 .. $#clsegs) { $tstclid=join('.',$tstclid,$segs[$_]); } ### DED 3/23/04 Must trim leading "." from tstclid $tstclid=substr($tstclid,1); if (($tstclid eq $clid) && ($segs[$#segs] eq $testid)) { if ($cnt < $segs[2]) { $cnt = $segs[2]; } } } } $cnt++; $nxtid=join('.','anon',"$cnt"); return $nxtid; } # ^ sac anonymous submission support sub get_test_sequence_for_reports { # Called with $CLIENT{'clid'},$FORM{'cndid'}, $FORM{'tstid'} # The pupose of this routine is to populate the global associative # arrays: TEST_SESSION SUBTEST_QUESTIONS SUBTEST_ANSWERS SUBTEST_RESPONSES SUBTEST_SUMMARY &get_test_profile($_[0], $_[2]); # populates the Assoc. array %TEST with the characteristics of the test (but not the questions or answers). $trash3 = join($pathsep, $testcomplete, "$_[0].$_[1].$_[2]"); $msg = ""; if ( ! open(TESTFILE,"<$trash3") ) { &logger::logerr("Unable to open $trash3: $!"); $msg="failed"; print "<!-- open failure\n$trash3\n$!\n-->\n"; $msg = ""; # Clear the hashs. Otherwise the calling code will process the current contents. %TEST_SESSION = () ; %SUBTEST_QUESTIONS = () ; %SUBTEST_ANSWERS = () ; %SUBTEST_RESPONSES = () ; %SUBTEST_SUMMARY = () ; } else { @seqlines = <TESTFILE>; close TESTFILE; $isubtest = 1; $iidx = 0; $iaryidx = 1; foreach $seqline (@seqlines) { chop ($seqline); if ($iidx eq 0) { # Process the first line of the Candidates test. @status = split(/&/, $seqline); $ifld = 0; $TEST_SESSION{'clid'} = $status[$ifld++]; # Client ID, like sandbox. $TEST_SESSION{'uid'} = $status[$ifld++]; # Candidate ID, like hank1 $TEST_SESSION{'tstid'} = $status[$ifld++]; # Test ID, like linux01 $TEST_SESSION{'state'} = $status[$ifld++]; # State, like 6.0.0 (for ???) $TEST_SESSION{'dscl'} = $status[$ifld++]; $TEST_SESSION{'profb'} = $status[$ifld++]; $TEST_SESSION{'id'} = $status[$ifld++]; # Test ID, like linux01 $TEST_SESSION{'profa'} = $status[$ifld++]; $TEST_SESSION{'srvy'} = $status[$ifld++]; $TEST_SESSION{'ntfy'} = $status[$ifld++]; $TEST_SESSION{'emlcnd'} = $status[$ifld++]; # Email address of candidate # Warning: The last two fields do not match the sample I looked at. @status = (); $iidx++; } else { if ($iaryidx eq 1) { $SUBTEST_QUESTIONS{$isubtest} = $seqline; } elsif ($iaryidx eq 2) { $SUBTEST_ANSWERS{$isubtest} = $seqline; } elsif ($iaryidx eq 3) { $seqline =~ s/\%0D\%0A/<br>/g; $SUBTEST_RESPONSES{$isubtest} = unmunge($seqline); } elsif ($iaryidx eq 4) { $SUBTEST_SUMMARY{$isubtest} = $seqline; } # The second and successive lines are treated as groups of four lines; 2-5, 6-9, etc. $iaryidx++; if ($iaryidx eq 5) { $iaryidx = 1; $isubtest++; } } } } @seqlines = (); return; } sub getPrograms { my ($client) = @_; my @programs = &get_data("programs.$client"); if (not @programs) { # no programs defined return undef; } chomp $programs[0]; my @fields = split(/&/,shift @programs); my $programs={}; foreach (@programs) { chomp $_; my $tmp = {}; @{$tmp}{@fields} = split(/&/,$_); $tmp->{'prglist'} = [split(/,/,$tmp->{'prglist'})]; $programs->{$tmp->{'prgid'}} = $tmp; $tmp = {}; } return $programs; } sub getGroups { # Parameters # $client - character string of the Client ID. # Returned value. # $groups is a reference to an un-named hash. # The keys of the hash are the group (Department) ids. # The values of the hash are other un-named hashs. # These other un-named hashs contain data for the group (Department). # The keys of the other hashs are the field ids in the groups file. # The values of the other hashs are the data, as a string, for the group. # But the value for the 'grplist' key is not a string. # The value for the 'grplist' key is a reference to an array. my ($client) = @_; my @groups = &get_data("groups.$client"); if (not @groups) { # no groups defined return undef; } chomp $groups[0]; my @fields = split(/&/,shift @groups); my $groups={}; foreach (@groups) { chomp $_; my $tmp = {}; @{$tmp}{@fields} = split(/&/,$_); # @fields is the list of field ids, and the keys to the anon. hash reffed by $tmp. my %user_ids = () ; foreach my $user_id (split(/,/,$tmp->{'grplist'})) { $user_ids{$user_id} = 1 ; } # Any user id will be listed only once. $tmp->{'grplist'} = [ sort keys %user_ids] ; $groups->{$tmp->{'grpid'}} = $tmp; $tmp = {}; } return $groups; } sub setGroups { my ($client,$groups) = @_; my @groups; my $grpfile = join($pathsep, $dataroot, "groups.$client"); if (not &file_exists($grpfile)) { my $grpheader = join($pathsep, $dataroot, "groups.std"); unless (&make_file( $grpfile, $grpheader, 1)) { &log_entry($SESSION{'clid'}, $SESSION{'uid'}, "3", "FC Error: $grpfile $grpheader"); return 0; } } @groups = &get_data("groups.$client"); chomp $groups[0]; my @fields = split(/&/,$groups[0]); @groups = ($groups[0]."\n"); foreach (values %$groups) { my @line = (); foreach my $fld (@fields) { if ($fld eq 'grplist') { push @line, join(',',@{$_->{'grplist'}}); } else { push @line, $_->{$fld}; } } push @groups, join('&',@line)."\n"; } open(TMPGRP, ">".join($pathsep, $dataroot, "groups.$client")) or return 0; print TMPGRP @groups; close TMPGRP; return 1; } sub getIdlist { # Parameters # $client - Client id as a string. # $grplist - string with comma separated group ids. # Returned value # $idlist is an un-named hash. # The keys of the hash are the candidates in the groups. # The values of the hash are 1. my ($client,$grplist) = @_; my $idlist; my $groups = &getGroups($client); foreach my $grp (split(/,/,$grplist)) { foreach my $cnd (@{$groups->{$grp}->{'grplist'}}) { $idlist->{$cnd} = 1; } } return $idlist; } sub getGroupMemberships { my ($client) = @_; my $groups = &getGroups($client); my $canidates = {}; foreach my $group (keys %$groups) { foreach my $canidate (@{$groups->{$group}->{'grplist'}}) { push @{$canidates->{$canidate}}, $group; } } return $canidates; } sub get_cnd_test_from_history { my ($dir,$clid,$cndid,$tstid,$testdate) = @_; my $testseconds = (defined $testdate ? toGMSeconds($testdate) : undef); my @seqlines = (); my $test_data; &get_test_profile($clid, $tstid); my $trash = join($pathsep, $dir, "$clid.$tstid.history"); $msg = ""; open(TESTFILE, "<$trash") or $msg="failed to open history file"; if ($msg eq "failed") { $msg = ""; return undef; } else { @seqlines = <TESTFILE>; my $entry; foreach (reverse @seqlines) { my @lines = split(/\<\<\>\>/, $_); my $timestamp = toGMSeconds($lines[0]); my %test_data; if (defined $testseconds and (abs($testseconds-$timestamp) > 5)) {next;} @test_data{'clid','uid','tstid','state','dscl','profb','id','profa','srvy','ntfy','emlcnd'} = split(/&/, $lines[1]); if ($test_data{'uid'} ne $cndid) {undef %test_data; next;} $test_data{'end'} = $test_data{'start'} = $timestamp; $test_data{'SUBTEST_QUESTIONS'} = $lines[2]; $test_data{'SUBTEST_ANSWERS'} = $lines[3]; $test_data{'SUBTEST_RESPONSES'} = $lines[4]; $test_data{'SUBTEST_SUMMARY'} = $lines[5]; $test_data = \%test_data; last; } } close TESTFILE; return $test_data; } sub get_cnd_test_cnt_from_history { # Get the number of times the candidate has taken the test. # The parameters are: # $dir - directory that contains the history files. # $clid - Client id. # $cndid - Candidate id. # $tstid - Test id. my ($dir,$clid,$cndid,$tstid) = @_; my @seqlines = (); my $test_count = 0; my $trash = join($pathsep, $dir, "$clid.$tstid.history"); $msg = ""; open(TESTFILE, "<$trash") or $msg="failed to open history file"; if ($msg eq "failed") { $msg = ""; return undef; } @seqlines = <TESTFILE>; close TESTFILE; my $entry; foreach (@seqlines) { my @lines = split(/\<\<\>\>/, $_); my %test_data; @test_data{'clid','uid','tstid','state','dscl','profb','id','profa','srvy','ntfy','emlcnd'} = split(/&/, $lines[1]); if ($test_data{'uid'} eq $cndid) {$test_count++;} } return $test_count; } sub get_users { my ($client,$test) = @_; my @users = &get_data("cnd.$client"); chomp ($users[0]); my @keys = split(/&/,shift(@users)); my %userdata; foreach my $user (@users) { chomp $user; $userdata{substr($user,0,index($user,"&"))} = $user; } if (defined $test) { my %tmp; my @filelist = &get_test_result_files($testcomplete, $client, $test); foreach my $file (@filelist) { my $user = $file; $user =~ s/.$test$//; $user =~ s/^$client.//; if (exists $userdata{$user}) { $tmp{$user} = $userdata{$user}; } } return (\%tmp); } else { return (\%userdata); } } sub build_number_select_list { my ($min, $max, $step) = @_; my $option_list = ""; if ($step eq "") { $step = 1; } if ($step eq "spread") { foreach my $i (1,5,10,20,25,50,100,250,500) { $option_list .= "<OPTION value=$i>$i</OPTION>\n"; } } else { for (my $i=$min;$i<=$max;$i += $step) { $option_list .= "<OPTION value=$i>$i</OPTION>\n"; } } return $option_list; } sub single_form_test_done { &put_several_questions(); my $passfailflag=&summarize_test($tsubtest); &put_test_sequence($testinprog, $TEST_SESSION{'clid'}, $TEST_SESSION{'uid'}, $TEST_SESSION{'id'}); &get_test_profile( $TEST_SESSION{'clid'}, $TEST_SESSION{'subtest'}); $TEST{'customexit'}=&check_for_custom_exit_file($passfailflag); $tstate = $TEST_STATES{'_COMPLETED'}; $tsubtest=0; $tqno=0; $TEST_SESSION{'state'} = "$tstate.$tsubtest.$tqno"; &put_test_sequence($testinprog, $TEST_SESSION{'clid'}, $TEST_SESSION{'uid'}, $TEST_SESSION{'id'}); &promote_test_sequence($testinprog, $testcomplete, $TEST_STATES{'_COMPLETED'}); if ($TEST{'ntfy'} ne '') { &get_test_profile( $TEST_SESSION{'clid'}, $TEST_SESSION{'id'}); &send_testresults("2","$endtime"); } &send_custom_exit_email($passfailflag); $TEST_SESSION{'navbuttons'}="<INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"$xlatphrase[769]\" onClick=\"cancel_test()\">"; &show_template($tetmplt); } sub put_several_questions { # multiple questions on same page my $tmpkey; &get_subtest_profile( $TEST_SESSION{'clid'}, $TEST_SESSION{'subtest'}); $TEST_SESSION{'noq'}=$SUBTEST{'noq'}; if ($_[0]) { $hitqno=$_[0]; } else { $hitqno=0; } for (keys %FORM) { if ($_ =~ /q[0-9]/ ) { ($tqno, $tqidx) = split(/\-/, $_); $tmpkey="$tqno-qcucmt"; $tqno =~ s/q//g; if ($tqno > $hitqno) { $hitqno = $tqno; } $tqidx =~ s/([0-9])//g; if ($tqidx eq 'qrs') { # setup qrs and qcucmt for putting $FORM{'qrs'} = $FORM{$_}; $FORM{'qcucmt'} = $FORM{$tmpkey}; #} elsif ($tqidx eq 'qcucmt') { ## setup qcucmt and qrs for putting ## haven't we already done this? ### Yup, we have. DED 9/21/04 #$FORM{'qcucmt'} = $FORM{$_}; #if ($FORM{'qcucmt'} ne '') { #$tmpkey =~ s/qcucmt/qrs/g; #$FORM{'qrs'} = $FORM{$tmpkey}; #} else { #next; #} } else { $tqidx =~ s/qrs//g; next; } $QUESTION{'id'} = &get_question_id($tsubtest, $tqno); &get_question_definition($TEST{'id'}, $CLIENT{'clid'}, $QUESTION{'id'}); &put_question_response($tsubtest, $tqno); } } $tqno=$hitqno; } sub check_for_custom_exit_file { my ($passfailflag) = @_; my $rec; my $customexitfile=join($pathsep,$questionroot,"$TEST_SESSION{'subtest'}.$SESSION{'clid'}.cx$passfailflag"); if (file_exists($customexitfile)) { if (open(TMPFILE,"<$customexitfile")) { my @cstextrecs=<TMPFILE>; close TMPFILE; my $customexistmsg=""; foreach $rec (@cstextrecs) { $customexistmsg=join('',$customexistmsg,$rec); } $TEST{'customexitmsg'}="$customexistmsg"; @cstextrecs=(); return "Y"; } } return "N"; } sub send_custom_exit_email { my ($passfailflag) = @_; my $rec; my $customemailfile=join($pathsep,$questionroot,"$TEST_SESSION{'subtest'}.$SESSION{'clid'}.ce$passfailflag"); if (file_exists($customemailfile)) { if (open(TMPFILE,"<$customemailfile")) { my @cstemlrecs=<TMPFILE>; close TMPFILE; $mmfrom = $CLIENT{'email_from'}; $mmto = $CANDIDATE{'eml'}; $mmsubj = $TEST{'desc'}." Completion Certificate"; my $customemailmsg=""; foreach $rec (@cstemlrecs) { $rec = &xlatline($rec, '', 0, 1); $customemailmsg=join('',$customemailmsg,$rec); } @cstemlrecs=(); $mmbody = ""; $htmlfile = "$SESSION{'clid'}.$SESSION{'uid'}.$TEST{'id'}.htm"; $mmbody = "MIME-version: 1.0\n" ; $mmbody .= "Content-type: text/html\n" ; $mmbody .= "Content-transfer-encoding: base64\n" ; $mmbody .= "Content-Disposition: attachment; filename=" ; $mmbody .= "\"${htmlfile}\"\n\n" ; # The second \n is required. $mmbody .= encode_base64($customemailmsg) ; # open(ATTACHFILE, "> /tmp/$htmlfile"); # print ATTACHFILE $customemailmsg; # close(ATTACHFILE); # `/usr/bin/uuencode /tmp/$htmlfile $htmlfile > /tmp/$htmlfile.uu`; # open(UUFILE, "/tmp/$htmlfile.uu"); # while (<UUFILE>) { # $mmbody = join('', $mmbody,$_); # } # close(UUFILE); # unlink("/tmp/$htmlfile"); # unlink("/tmp/$htmlfile.uu"); &send_mail($mmfrom, $mmto, $mmsubj, $mmbody); } } } sub resend_exit_emails { my ($clid, $cndid, $testid) = @_; &get_candidate_profile($clid, $cndid); $TEST_SESSION{'subtest'} = $testid; &get_test_sequence_for_reports( $clid, $cndid, $testid); &get_subtest_profile( $clid, $testid); my $passfailflag=&summarize_test(2); my $mtime = (stat($testcomplete.$pathsep.$clid.".".$cndid.".".$testid))[9]; $endtime = &format_date_time("h:nn:ss", "2", "-10000", $mtime); $enddate = &format_date_time("dd-mmm-yyyy", "2", "-10000", $mtime); &send_testresults("2", $endtime, $enddate); &send_custom_exit_email($passfailflag); } sub redirect { my $location = $_[0]; my %vars = %{$_[1]}; my $vars = ""; if (scalar keys %vars != 0) { foreach (keys %vars) { $vars .= "&".$_."=".$vars{$_}; } $vars =~ s/^&/\?/; } if ($ENV{'HTTPS'} eq "on") { $url = "https://"; } else { $url = "http://"; } $url .= $ENV{'HTTP_HOST'}; $url .= "/cgi-bin/".$location.".pl$vars"; warn "ReDirect to $url ." if ($HBI_Debug_redirect) ; print "Location: $url\n\n"; } # end with True because this is a require file 1