[system] / trunk / webwork / system / scripts / FILE.pl Repository:
ViewVC logotype

Annotation of /trunk/webwork/system/scripts/FILE.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (view) (download) (as text)

1 : sam 2 #!/usr/bin/perl
2 :    
3 :     ## $Id$
4 :    
5 :     ####################################################################
6 :     # Copyright @ 1995-1998 University of Rochester
7 :     # All Rights Reserved
8 :     ####################################################################
9 :    
10 :     # #############################################################
11 :     # #############################################################
12 :     # File: FILE.pl
13 :     # This contains the subroutines for creating problem files,
14 :     # recording scores, printing delimited files, etc.
15 :     # #############################################################
16 :     # #############################################################
17 :     use strict;
18 :    
19 :    
20 :     # Variables global to this file
21 :    
22 :     my $scoringDirectory = getCourseScoringDirectory();
23 :     my $templateDirectory = getCourseTemplateDirectory();
24 :     my $scriptDirectory = getWebworkScriptDirectory();
25 :     my $databaseDirectory = getCourseDatabaseDirectory();
26 :    
27 :     my $DELIM = $Global::delim;
28 :     my $scoreFilePrefix = $Global::scoreFilePrefix;
29 :     my $scoring_log = $Global::scoring_log;
30 :     my $dash = $Global::dash;
31 :     my $DAT = $Global::dat;
32 :     my @STATUS_DROP = @Global::statusDrop;
33 :    
34 :     my $dd = getDirDelim();
35 :    
36 :    
37 :    
38 :     # Subroutines:
39 :    
40 :    
41 :     #sub createProblem {
42 :     # my($mode,$probNum,$psvn,$courseName,$sourceref,$refSubmittedAnswers)=@_;
43 :     # my @out;
44 :     # #&attachProbSetRecord($psvn);
45 :     # my $fileName = &getProblemFileName($probNum,$psvn);
46 :     # #print "content-type: text/plain\n\ngetProblemFileName gives $fileName\n probNum =$probNum and psvn =$psvn\n";
47 :     # $fileName = $main::in{'probFileName'} if defined($main::in{'probFileName'});
48 :     # #print "now fileName gives $fileName\n probNum =$probNum and psvn =$psvn\n";
49 :     #
50 :     ####Define global variables for the interpreter and seed random function
51 :     # #srand(&getProblemSeed($probNum,$psvn));
52 :     ## print "\n\nContent-type: text/html\n\nERROR: createProblem: Submitted Answers list |$refSubmittedAnswers| not passed\n\n"
53 :     ## unless defined($refSubmittedAnswers);
54 :     # defineProblemVars($mode,$probNum, $psvn,$courseName,$refSubmittedAnswers);
55 :     # @out = &createLines($mode,$fileName,$sourceref);
56 :     #}
57 :     #
58 :     #sub createProblem2 {
59 :     # my ($mode,$probNum,$psvn,$courseName,$sourceref,$refSubmittedAnswers)=@_;
60 :     # my %envir=defineProblemEnvir($mode,$probNum,$psvn,$courseName,$refSubmittedAnswers);
61 :     # #print %envir;
62 :     # createPGtext($sourceref,\%envir);
63 :     #}
64 :     #
65 :    
66 :     ########
67 :     ## Where is createNumberedInsert used???
68 :     ########
69 :     # sub createNumberedInsert {
70 :     # my($mode,$probNum,$psvn)=@_;
71 :     # my @out;
72 :     # # &attachProbSetRecord($psvn);
73 :     # my $fileName = &getInsertFileName($num,$psvn);
74 :     #
75 :     # ###Define global variables for the interpreter
76 :     # defineProblemVars($mode,$probNum, $psvn,$refSubmittedAnswers);
77 :     # @out = &createLines($mode, $fileName);
78 :     # }
79 :    
80 :     #This subroutine has been substituted by createProblem, because it is
81 :     # virtually identical to it
82 :     #sub createInsert {
83 :     # my($mode,$fileName,$psvn,$courseName,$sourceref)=@_;
84 :     # my @out;
85 :     # # &attachProbSetRecord($psvn);
86 :     #
87 :     #
88 :     # ###Define global variables for the interpreter
89 :     # # This is for the probSet.pl page so $probNum is not well defined
90 :     # my $probNum = 0;
91 :     # defineProblemVars($mode,$probNum, $psvn,$courseName);
92 :     # @out = &createLines($mode,$fileName,$sourceref);
93 :     #}
94 :    
95 :     #sub defineProblemVars {
96 :     # my ($mode,$probNum,$psvn,$courseName,$refSubmittedAnswers) = @_;
97 :     # @main::submittedAnswers = @$refSubmittedAnswers if defined($refSubmittedAnswers);
98 :     # $main::psvnNumber = $psvn;
99 :     # $main::psvn = $psvn;
100 :     # $main::studentName = &getStudentName($psvn);
101 :     # $main::studentLogin = &getStudentLogin($psvn);
102 :     # $main::sectionName = &getClassSection($psvn);
103 :     # $main::sectionNumber = &getClassSection($psvn);
104 :     # $main::setNumber = &getSetNumber($psvn);
105 :     # $main::questionNumber = $probNum;
106 :     # $main::probNum = $probNum;
107 :     # $main::openDate = &getOpenDate($psvn);
108 :     # $main::formatedOpenDate = &formatDateAndTime(&getOpenDate($psvn));
109 :     # $main::dueDate = &getDueDate($psvn);
110 :     # $main::formatedDueDate = &formatDateAndTime(&getDueDate($psvn));
111 :     # $main::answerDate = &getAnswerDate($psvn);
112 :     # $main::formatedAnswerDate = &formatDateAndTime(&getAnswerDate($psvn));
113 :     # $main::problemValue = &getProblemValue($probNum,$psvn);
114 :     # $main::fileName = &getProblemFileName($probNum,$psvn);
115 :     # $main::probFileName = &getProblemFileName($probNum,$psvn);
116 :     # $main::templateDirectory = &getCourseTemplateDirectory();
117 :     # $main::languageMode = $mode;
118 :     # $main::outputMode = $mode;
119 :     # $main::courseName = $courseName;
120 :     # $main::sessionKey = ( defined($main::in{'key'}) ) ?$main::in{'key'} : " ";
121 :     # #my $seed ;
122 :     # #if ( defined( $inputs{'seed'}) && $permissions == $Global::instructor_permissions ) {
123 :     # # $seed = $inputs{'seed'};
124 :     # #} else {
125 :     # # $seed = &getProblemSeed($probNum, $psvn);
126 :     # #}
127 :     # #$main::problemSeed = $seed;
128 :     # ##Move srand to PGeval, after unpacking it
129 :     # #srand($main::problemSeed);
130 :     #
131 :     #}
132 :    
133 :    
134 :     ###no longer use this subroutine
135 :     ###createPGtext calls PGeval directly
136 :     ###the language is figured out in the processProblem.pl
137 :     #sub createLines {
138 :     #
139 :     # my ($mode,$fileName,$sourceref) = @_;
140 :     # my @out;
141 :     #
142 :     #
143 :     ### Set current directory
144 :     ## my $pathName = $fileName;
145 :     ## $pathName =~ s|[^/]*$||;
146 :     ## my $currentDirectory = ${templateDirectory} . ${pathName};
147 :     ## chdir "$currentDirectory";
148 :     ##
149 :     ## if (! open(INPUT, "${templateDirectory}$fileName") ) {
150 :     ### If the file can not be found and opened output an error message
151 :     ## push(@out, "createLines: ERROR: Can't open filename ${templateDirectory}$fileName\n");
152 :     # }
153 :     # else {
154 :     #
155 :     #
156 :     ### Determine language
157 :     # # print "content-type: text/plain\n\n fileName = $fileName\n";
158 :     # $fileName =~ /\.([^\.]*)$/;
159 :     # my $languageType = $1;
160 :     # #print "languageType=$languageType<BR>\n";
161 :     ### Call interpreter
162 :     # if ($languageType eq 'qz') {
163 :     # ##Assign INPUT to problem file
164 :     # require "${scriptDirectory}qz2sub.pl";;
165 :     # @out = &qz2($mode);
166 :     #
167 :     # @out = post_process_qz($mode, \@out);
168 :     # } elsif ($languageType eq 'pg') {
169 :     # #$languageMode = $mode; #Define global variables for the interpreter and seed random function
170 :     # @out =&PGeval($sourceref);
171 :     # } else {
172 :     # $out[0] = "ERROR: createLines: Don't understand languages with extension $languageType.<BR>\n";
173 :     # }
174 :     ## }
175 :     # @out;
176 :     #}
177 :     #
178 :    
179 :    
180 :     #sub post_process_qz {
181 :     # my ($mode,$refInput_lines) = @_;
182 :     # my $col = 70;
183 :     # my $len = 0.07*$col;
184 :     # my @output_lines = ();
185 :     # my $ansName = "";
186 :     # my $answerValue = "";
187 :     # my $ansCount = 0;
188 :     # my $line;
189 :     # foreach $line (@$refInput_lines) {
190 :     #
191 :     # if ($line =~ /^\[ans/i) {
192 :     #
193 :     # $ansCount++;
194 :     # $ansName = "answer" . "$ansCount";
195 :     # $answerValue = param("$ansName") if defined param("$ansName");
196 :     # #print "<BR>$ansName<BR>";
197 :     # if ($mode eq 'HTML') {
198 :     # push(@output_lines, qq(<INPUT TYPE="TEXT" NAME=$ansName VALUE="$answerValue" SIZE="$col" MAXLENGTH="800">\n\n<HR>) );
199 :     # } elsif ($mode eq 'Latex2HTML') {
200 :     # push(@output_lines, qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"answer$ansCount\" VALUE = \"$main::submittedAnswers[$ansCount]\">\n\\end{rawhtml}\n! );
201 :     #
202 :     # } elsif ($mode eq 'TeX') {
203 :     # push(@output_lines, "\rule{${len}in}{.01in}" );
204 :     # } else {
205 :     # push(@output_lines, "ERROR: post_process_qz: mode=$mode is not recognized");
206 :     # }
207 :     # push(@main::PG_ANSWERS, create_qz_ans_function($line));
208 :     # } else {
209 :     # push(@output_lines, $line);
210 :     # }
211 :     # }
212 :     #
213 :     # @output_lines;
214 :     #}
215 :     #sub create_qz_ans_function{
216 :     # my $line = shift @_;
217 :     # my $answer_evaluator = 0;
218 :     # my ($format, $correctAnswer) = split("=",$line);
219 :     #
220 :     # if ($format =~ /^\[ans:([0-9]*),?([0-9\.\-\+eE]*)%?/ ) { #numeric compare--the guts of std_num_cmp
221 :     # my $accuracy = $2;
222 :     # my $precision=$1;
223 :     # my $relpercentTol = $2;
224 :     # $relpercentTol = .01 unless($relpercentTol);
225 :     # my $tol = .01*$relpercentTol;
226 :     # my $formattedCorrectAnswer = sprintf("%10.${precision}g",$correctAnswer );
227 :     # $answer_evaluator = sub {
228 :     # my $in = shift @_;
229 :     # my $formattedSubmittedAnswer = "";
230 :     # my $PGanswerMessage = "";
231 :     # my ($inVal,$correctVal);
232 :     # $correctVal = eval($correctAnswer);
233 :     # $@='';
234 :     # $inVal = eval($in);
235 :     # if ($@) { ##error message from eval
236 :     # $formattedSubmittedAnswer = $@;
237 :     # $formattedSubmittedAnswer =~ s/at.*line [\d]*//g;
238 :     # $formattedSubmittedAnswer =~ s/called//g;
239 :     # $formattedSubmittedAnswer =~ s/&main:://g;
240 :     # $formattedSubmittedAnswer =~ s/chunk [\d]*//g;
241 :     # } else {
242 :     # $formattedSubmittedAnswer = sprintf($format,$inVal);
243 :     # }
244 :     #
245 :     # if ($correctVal == 0) {
246 :     # $tol = 1E-12; ## want $tol to be non zero
247 :     # } else {
248 :     # $tol = abs($tol*$correctVal);
249 :     # }
250 :     # my $correctQ =0;
251 :     # $correctQ = 1 if ((not $@) and
252 :     # (abs( $inVal - $correctVal ) <= $tol));
253 :     # if ($@) {
254 :     # $PGanswerMessage = "There is a syntax error in your answer";
255 :     # }
256 :     # ($correctQ,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage);
257 :     # };
258 :     #
259 :     # } else { #string compare use the guts of str_cmp to accomplish this
260 :     #
261 :     # my $normalizedCorrectAnswer = $correctAnswer;
262 :     # # normalize the correct answer:
263 :     # $normalizedCorrectAnswer=~ s/s*$//; # remove trailing whitespace
264 :     # $normalizedCorrectAnswer=~ s/s+/ /g; # replace double spaces by single space
265 :     # $normalizedCorrectAnswer=~ tr/a-z/A-Z/; # Make letters uppercase
266 :     # $normalizedCorrectAnswer=~ s/^s*//; # remove initial spaces
267 :     # my $PGanswerMessage = "";
268 :     # $answer_evaluator = sub {
269 :     # my $in = shift @_;
270 :     # my $originalAnswer = $in;
271 :     # $in =~ s/s*$//; # remove trailing whitespace
272 :     # $in =~ s/s+/ /g; # replace double spaces by single space
273 :     # $in =~ tr/a-z/A-Z/; # Make letters uppercase
274 :     # # why is there no removing of the initial spaces here?
275 :     # my $correctQ =0;
276 :     # $correctQ = 1 if $in eq $normalizedCorrectAnswer;
277 :     # ($correctQ,$correctAnswer,$originalAnswer,$PGanswerMessage);
278 :     # };
279 :     #
280 :     #
281 :     # }
282 :     # $answer_evaluator;
283 :     #}
284 :     ############################## SCORING FILES ROUTINES ############################
285 :     #sub recordProblemAnswer {
286 :     # my ($in, $num,$user, $psvn)=@_;
287 :     # # &attachProbSetRecord($psvn);
288 :     # my($setNumber)=&getSetNumber($psvn);
289 :     # my ($scoreFileName)="${databaseDirectory}$scoreFilePrefix$setNumber$dash${psvn}.sco";
290 :     # unless (-e $scoreFileName)
291 :     # {&createFile($scoreFileName, $Global::sco_files_permission, $Global::numericalGroupID);}
292 :     # open(TEMP_FILE,">>$scoreFileName") ||
293 :     # print "Couldn't record answer in $scoreFileName";
294 :     # # my $time = time; # add time stamp -- should we make this human readable?
295 :     # my $time = &formatDateAndTime(time); # add time stamp
296 :     #
297 :     # print TEMP_FILE "$num $DELIM $in $DELIM $user $DELIM $time\n";
298 :     # close(TEMP_FILE);
299 :     # if ($in eq 'Y') {&putProblemNumOfCorrectAns(&getProblemNumOfCorrectAns($num,$psvn)+1,$num,$psvn);}
300 :     # if ($in eq 'N') {&putProblemNumOfIncorrectAns(&getProblemNumOfIncorrectAns($num,$psvn)+1,$num,$psvn);}
301 :     # unless (defined(&getProblemStatus($num)) and (&getProblemStatus($num) eq 'Y')) {
302 :     # &putProblemStatus($in,$num,$psvn);
303 :     # }
304 :     # &detachProbSetRecord($psvn);
305 :     # };
306 :    
307 :     #sub getRecordedScores {
308 :     #
309 :     # my ($Yarrayref,$Narrayref,$psvn) = @_;
310 :     # &attachProbSetRecord($psvn);
311 :     # my $setNumber = &getSetNumber($psvn);
312 :     # &detachProbSetRecord($psvn);
313 :     # my ($scoreFileName)="${databaseDirectory}$scoreFilePrefix$setNumber$dash${psvn}.sco";
314 :     # #print "Reading from file $scoreFileName\n" if $debugON;
315 :     # if ( open(SCORE_FILE,"<$scoreFileName") ) {
316 :     # while (<SCORE_FILE>) {
317 :     # my @temp=split(/$DELIM/,$_);
318 :     # if ($temp[1]=~/Y/) {$$Yarrayref[$temp[0]]++;}
319 :     # elsif ($temp[1]=~/N/) {$$Narrayref[$temp[0]]++;}
320 :     # else {wwerror("$0", "corrupted $scoreFileName");}
321 :     # };
322 :     # close(SCORE_FILE);
323 :     # } else {
324 :     # warn "Warning: Couldn't open $scoreFileName. Will continue.\n";
325 :     # }
326 :     # # OPERATES ON THE ARRAYS Yarray and Narray.
327 :     #}
328 :    
329 :     sub round_score {
330 :     my $num = shift;
331 :     my $rounding_dem = 10**$Global::score_decimal_digits;
332 :     int($num*$rounding_dem + .5)/$rounding_dem;
333 :     }
334 :    
335 :     ######################## END SCORING FILES ROUTINES ###########################
336 :     ############
337 :     ### SMD - subroutine to get the number of answers from a specific file
338 :     ### - this handles questions which have more than one answer field.
339 :     ### - called with a problem number ($probNum)
340 :     ### - returns number of answers
341 :     #sub getNumberofAns
342 :     # {
343 :     # local($problemnumber)=@_;
344 :     # $numberofAnswers=0;
345 :     # local($filename)= &getProblemFileName($problemnumber);
346 :     # open(FILE, "${templateDirectory}$filename");
347 :     # while(<FILE>)
348 :     # {
349 :     # if ($_ =~ /^\s*\[ans/)
350 :     # {
351 :     # $numberofAnswers++;
352 :     # }
353 :     # }
354 :     # close FILE;
355 :     # $numberofAnswers;
356 :     # }
357 :     ### GAGE 8/23/96
358 :     #sub getNumberofSubmittedAns {
359 :     # my $i = 1;
360 :     # while (defined($inputs{"answer$i"}) ) #inputs can't be sytactically local (using my)
361 :     # {$i++};
362 :     # $i--; # the off-by-one problem
363 :     # $i;
364 :    
365 :     ###########
366 :    
367 :     sub readSetDef {
368 :     my ($fileName) = @_;
369 :     my $setNumber = '';
370 :     my $shortFileName = fileFromPath($fileName);
371 :     if ($shortFileName =~ m|^set(\w+)\.def$|) {$setNumber = $1;}
372 :     else {
373 :     wwerror("$0", "The setDefinition file name must begin with <CODE>set</CODE>
374 :     and must end with <CODE>.def</CODE> . Every thing in between becomes the name of the set.
375 :     For example <CODE>set1.def</CODE>, <CODE>setExam.def</CODE>, and <CODE>setsample7.def</CODE>
376 :     define sets named <CODE>1</CODE>, <CODE>Exam</CODE>, and <CODE>sample7</CODE> respectively. The
377 :     filename, $shortFileName, you entered is not legal\n");
378 :     }
379 :    
380 :     my ($line,$name,$value,$attemptLimit);
381 :     open (SETFILENAME, "$fileName") or wwerror("$0", "Can't open file $fileName\n");
382 :     my $setHeaderFileName = '';
383 :     my $probHeaderFileName = '';
384 :     my @problemList=();
385 :     my @problemValueList=();
386 :     my @problemAttemptLimitList=();
387 :     my ($dueDate,$openDate,$answerDate);
388 :     my ($problemListref,$problemValueListref,$problemAttemptLimitListref);
389 :     while (<SETFILENAME>) {
390 :     chomp($line = $_);
391 :     $line =~ s|(#.*)||; ## don't read past comments
392 :     unless ($line =~ /\S/) {next;} ## skip blank lines
393 :     $line =~ s|\s*$||; ## trim trailing spaces
394 :     $line =~ m|^\s*(\w+)\s*=\s*(.*)|;
395 :     if ($1 eq 'setNumber') {next;}
396 :     elsif ($1 eq 'paperHeaderFile') {$setHeaderFileName = $2;}
397 :     elsif ($1 eq 'screenHeaderFile') {$probHeaderFileName = $2;}
398 :     elsif ($1 eq 'dueDate') {$dueDate = $2;}
399 :     elsif ($1 eq 'openDate') {$openDate = $2;}
400 :     elsif ($1 eq 'answerDate') {$answerDate = $2;}
401 :     elsif ($1 eq 'problemList') {last;}
402 :     else {wwerror("$0", "readSetDef error, can't read the line: $line");}
403 :     }
404 :    
405 :     my $time1 = &unformatDateAndTime($openDate);
406 :     my $time2 = &unformatDateAndTime($dueDate);
407 :     my $time3 = &unformatDateAndTime($answerDate);
408 :     if ($time2 < $time1 or $time3 < $time2) {
409 :     &Global::error('File.pl: readSetDef error', "The open date: $openDate, due date: $dueDate, and answer date: $answerDate
410 :     must be in chronologicasl order.");
411 :     }
412 :    
413 :     $setHeaderFileName =~ s/(.*?)\s*$/$1/; #remove trailing white space
414 :     $probHeaderFileName =~ s/(.*?)\s*$/$1/; #remove trailing white space
415 :    
416 :     # print "setNumber: $setNumber\ndueDate: $dueDate\nopenDate: $openDate\nanswerDate: $answerDate\n";
417 :     while(<SETFILENAME>) {
418 :     chomp($line=$_);
419 :     $line =~ s/(#.*)//; ## don't read past comments
420 :     unless ($line =~ /\S/) {next;} ## skip blank lines
421 :    
422 :     ($name, $value, $attemptLimit) = split (/\s*,\s*/,$line);
423 :     $name =~ s/\s*//g;
424 :     push(@problemList, $name);
425 :     $value = "" unless defined($value);
426 :     $value =~ s/[^\d]*//g;
427 :     unless ($value =~ /\d+/) {$value = 1;}
428 :     push(@problemValueList, $value);
429 :     $attemptLimit = "" unless defined($attemptLimit);
430 :     $attemptLimit =~ s/[^\d-]*//g;
431 :     unless ($attemptLimit =~ /\d+/) {$attemptLimit = -1;}
432 :    
433 :     push(@problemAttemptLimitList, $attemptLimit);
434 :     }
435 :     close(SETFILENAME);
436 :     #print "problemList: @problemList\n";
437 :     #print "problemValueList: @problemValueList\n";
438 :     #print "problemAttemptLimitList: @problemAttemptLimitList\n";
439 :     $problemListref = \@problemList;
440 :     $problemValueListref = \@problemValueList;
441 :     $problemAttemptLimitListref = \@problemAttemptLimitList;
442 :     ($setNumber,$setHeaderFileName,$probHeaderFileName,$dueDate,$openDate,$answerDate,$problemListref,$problemValueListref,$problemAttemptLimitListref);
443 :     }
444 :    
445 :     sub max { ## find the max element of array
446 :     my $out = $_[0];
447 :     my $num;
448 :     foreach $num (@_) {
449 :     if ((defined $num) and ($num > $out)) {$out = $num;}
450 :     }
451 :     $out;
452 :     }
453 :    
454 :     sub min { ## find the max element of array
455 :     my $out = $_[0];
456 :     my $num;
457 :     foreach $num (@_) {
458 :     if ((defined $num) and ($num < $out)) {$out = $num;}
459 :     }
460 :     $out;
461 :     }
462 :    
463 :     sub getFieldLengths {
464 :    
465 :     ## takes as a parameter the reference to a delimited array
466 :     ## (such as you would get by reading in a delimited file)
467 :     ## where each element is a line from a delimited file.
468 :     ## returns an array which holds
469 :     ## the maximum field lengths in the file.
470 :    
471 :     my ($datFileArray_ref)=@_;
472 :     my($i);
473 :     my(@datArray,@fieldLength,@datFileArray, $line);
474 :     @fieldLength=();
475 :     @datFileArray=@$datFileArray_ref;
476 :    
477 :     foreach $line (@datFileArray) { ## read through file and get field lengths
478 :     unless ($line =~ /\S/) {next;} ## skip blank lines
479 :     chomp $line;
480 :     @datArray=&getRecord($line);
481 :     for ($i=0; $i <=$#datArray; $i++) {
482 :     $fieldLength[$i] = 0 unless defined $fieldLength[$i];
483 :     $fieldLength[$i]=&max(length("$datArray[$i]"),$fieldLength[$i]);
484 :     }
485 :     }
486 :     return (@fieldLength);
487 :     }
488 :    
489 :    
490 :     sub columnArrayArrange {
491 :    
492 :     ## takes as a parameter a delimited array
493 :     ## (such as you would get by reading in a delimited file)
494 :     ## where each element is a line from a delimited file.
495 :    
496 :     # Outputs an array which adds
497 :     # extra space if necessary to the fields so that all columns line up.
498 :     # The widest field in any column will contain exactly 1 spaces at the
499 :     # end of the (non space characters of the) field. For example
500 :     # ",a very long field entry ," at one extreme and ", ," at the other
501 :    
502 :     my @inFile=@_;
503 :     my($i,$tempFileName,$datString,$line);
504 :     my @outFile =();
505 :     my(@fieldLength,@datArray);
506 :     $i=1;
507 :    
508 :     @fieldLength=&getFieldLengths(\@inFile);
509 :     foreach $line (@inFile) { ## read through file array and get field lengths
510 :     unless ($line =~ /\S/) {next;} ## skip blank lines
511 :     chomp $line;
512 :     @datArray=&getRecord($line);
513 :     for ($i=0; $i <=$#datArray; $i++) {
514 :     $datArray[$i].=(" " x ($fieldLength[$i]+1-length("$datArray[$i]")));
515 :     }
516 :     $datString=join("${DELIM}",@datArray);
517 :     push @outFile , "$datString\n";
518 :     }
519 :     @outFile;
520 :     }
521 :    
522 :    
523 :     sub columnPrint {
524 :    
525 :     # Takes two parameters. The first is the filename of the
526 :     # delimited input file. The second is the name of the
527 :     # output file (these names may be the same). The permissions
528 :     # and group of the output file will be the same as the
529 :     # input file
530 :    
531 :     # Takes any delimited (with \$DELIM delimiters) file and adds
532 :     # extra space if necessary to the fields so that all columns line up.
533 :     # The widest field in any column will contain exactly 2 spaces at the
534 :     # end of the (non space characters 0f the) field. For example
535 :     # ",a very long field entry ," at one extreme and ", ," at the other
536 :     #
537 :     my($inFileName,$outFileName)=@_;
538 :     my($line);
539 :    
540 :     my ($permission, $gid) = (stat($inFileName))[2,5];
541 :     $permission = ($permission & 0777); ##get rid of file type stuff
542 :    
543 :     open(INFILE,"$inFileName") or wwerror("$0","can't open $inFileName for reading");
544 :     my @inFile=<INFILE>;
545 :     close(INFILE);
546 :    
547 :     &createFile($outFileName, $permission, $gid);
548 :    
549 :     my @outFile = &columnArrayArrange(@inFile);
550 :    
551 :     open(OUTFILE,">$outFileName") or wwerror("$0","can't open $outFileName for writing");
552 :     foreach $line (@outFile) {print OUTFILE $line;}
553 :     close(OUTFILE);
554 :     }
555 :    
556 :     sub getRecord
557 :    
558 :     # Takes a delimited line as a parameter and returns an
559 :     # array. Note that all white space is removed. If the
560 :     # last field is empty, the last element of the returned
561 :     # array is also empty (unlike what the perl split command
562 :     # would return). E.G. @lineArray=&getRecord(\$delimitedLine).
563 :     {
564 :     my $DELIM = $Global::delim;
565 :     my($line) = $_[0];
566 :     my(@lineArray);
567 :     $line.='A'; # add 'A' to end of line so that
568 :     # last field is never empty
569 :     @lineArray = split(/\s*${DELIM}\s*/,$line);
570 :     $lineArray[$#lineArray] =~s/\s*A$//; # remove spaces and the 'A' from last element
571 :     $lineArray[0] =~s/^\s*//; # remove white space from first element
572 :     @lineArray;
573 :     }
574 :    
575 :    
576 :    
577 :    
578 :     sub delim2aa {
579 :    
580 :     # Takes a delimited file as a parameter and returns an
581 :     # associative array with the first field as the key.
582 :     # Blank lines are skipped. White space is removed
583 :    
584 :     my $fileName =$_[0];
585 :     my(@dbArray,$key,%assocArray,$dbString);
586 :     open(FILE, "$fileName") or wwerror("$0","can't open $fileName");
587 :     while (<FILE>)
588 :     {
589 :     unless ($_ =~ /\S/) {next;} ## skip blank lines
590 :     chomp;
591 :     @dbArray=&getRecord($_);
592 :     $key=shift(@dbArray);
593 :     $dbString=join("${DELIM}",@dbArray);
594 :     $assocArray{$key}=$dbString;
595 :     }
596 :     close(FILE);
597 :     %assocArray;
598 :     }
599 :     sub dropStatus
600 :    
601 :     # Takes one parameter \$status and returns 1 if \$status matches a word in the
602 :     # \@STATUS_DROP global array, 0 otherwise. E.G. if ($dropStatus(\$status) {...}
603 :     # where \$status is the entry in the status field of the class list. \@STATUS_DROP
604 :     # is a global array defined in webwork.ph
605 :     {
606 :     my($tag) = 0;
607 :     my($status) = $_[0];
608 :     my($statusItem);
609 :     foreach $statusItem (@STATUS_DROP)
610 :     {
611 :     if ($status =~ /^\s*$statusItem\s*$/i) {$tag = 1;}
612 :    
613 :     }
614 :     $tag;
615 :     }
616 :     ##########################Basem's additions####################
617 :     ##Gives a nice list of ALL problem sets using radio buttons as default.
618 :     ##So to make a form with radio buttoned sets, simply start the form on the
619 :     ##line before calling printProbSets and a line after for the submit. The
620 :     ##default CGI value that is passed is the probSetKey. To make it the setNo,
621 :     ##call this subroutine: &printProbSets("setNo")
622 :     ###############################################################
623 :     #sub printProbSets {
624 :     # my ($passFlag,$pHash)=@_;
625 :     # my %setNumberHash = %$pHash;
626 :     # my @sortedSetNumberKeys=&sortSetNamesByDueDate($pHash);
627 :     # my @problemDates = ();
628 :     # my $problemDateLine;
629 :     # my ( $probSetKey, $odts,$ddts,$adts,$timeNow,$DueDate,$AnswerDate,$OpenDate);
630 :     # my $sortedSetNumber;
631 :     #
632 :     # foreach $sortedSetNumber(@sortedSetNumberKeys) {
633 :     # $probSetKey=$setNumberHash{$sortedSetNumber};
634 :     # &attachProbSetRecord($probSetKey);
635 :     # $odts=&getOpenDate($probSetKey);
636 :     # $ddts=&getDueDate($probSetKey);
637 :     # $adts=&getAnswerDate($probSetKey);
638 :     # $timeNow = time;
639 :     #
640 :     # $DueDate=&formatDateAndTime($ddts);
641 :     # $AnswerDate = &formatDateAndTime($adts);
642 :     # $OpenDate = &formatDateAndTime($odts);
643 :     #
644 :     # # prepare message based on current time relative to the Open, Due and Answer dates.
645 :     # $problemDateLine = "";
646 :     #
647 :     # if ($passFlag eq "setNo") {
648 :     # $problemDateLine = "\n <INPUT NAME=\"setNo\"
649 :     # TYPE=\"radio\" VALUE=\"$sortedSetNumber\"> ";
650 :     # }
651 :     # else {
652 :     # $problemDateLine = "\n <INPUT NAME=\"probSetKey\"
653 :     # TYPE=\"radio\" VALUE=\"$setNumberHash{$sortedSetNumber}\"> ";
654 :     # }
655 :     #
656 :     # $problemDateLine .= "Problem Set Number $sortedSetNumber";
657 :     # ($timeNow < $odts ) && do {$problemDateLine .= &beforeOpenDateMsg($OpenDate) .
658 :     # &problemDates($OpenDate,$DueDate,$AnswerDate);};
659 :     # ( $odts <= $timeNow ) && ($timeNow < $ddts) &&
660 :     # do {$problemDateLine .= &afterOpenDateMsg($DueDate) .
661 :     # &problemDates($OpenDate,$DueDate,$AnswerDate);};
662 :     # ( $ddts <= $timeNow ) && ($timeNow < $adts) &&
663 :     # do {$problemDateLine .=
664 :     # &afterDueDateMsg($AnswerDate) . &problemDates($OpenDate,$DueDate,$AnswerDate);};
665 :     # ( $adts <= $timeNow ) &&
666 :     # do {$problemDateLine .=
667 :     # &afterAnswerDateMsg .&problemDates($OpenDate,$DueDate,$AnswerDate);};
668 :     # push (@problemDates, $problemDateLine);
669 :     # }
670 :     #
671 :     # print join("\n\n", @problemDates),"\n"; # include open/due/answer dates
672 :     # # prepared above
673 :     #}
674 :     #
675 :     ##########################Basem's additions####################
676 :     ##Does the same thing as printProbSets but in the abbreviated style used in
677 :     ##welcome.pl
678 :     ###############################################################
679 :     #sub printProbSetsJR {
680 :     # my ($passFlag,$pHash)=@_;
681 :     # my %setNumberHash = %$pHash;
682 :     # my @sortedSetNumberKeys=sort keys(%setNumberHash);
683 :     # my @problemDates = ();
684 :     # my $problemDateLine;
685 :     # my ( $probSetKey, $odts,$ddts,$adts,$timeNow,$DueDate,$AnswerDate,$OpenDate);
686 :     # my $sortedSetNumber;
687 :     #
688 :     # foreach $sortedSetNumber(@sortedSetNumberKeys) {
689 :     # $probSetKey=$setNumberHash{$sortedSetNumber};
690 :     # &attachProbSetRecord($probSetKey);
691 :     # $odts=&getOpenDate($probSetKey);
692 :     # $ddts=&getDueDate($probSetKey);
693 :     # $adts=&getAnswerDate($probSetKey);
694 :     # $timeNow = time;
695 :     #
696 :     # $DueDate=&formatDateAndTime($ddts);
697 :     # $AnswerDate = &formatDateAndTime($adts);
698 :     # $OpenDate = &formatDateAndTime($odts);
699 :     #
700 :     # # prepare message based on current time relative to the Open, Due and Answer dates.
701 :     # $problemDateLine = "";
702 :     #
703 :     # if ($passFlag eq "setNo") {
704 :     # $problemDateLine = "\n <INPUT NAME=\"setNo\"
705 :     # TYPE=\"radio\" VALUE=\"$sortedSetNumber\"> ";
706 :     # }
707 :     # else {
708 :     # $problemDateLine = "\n <INPUT NAME=\"probSetKey\"
709 :     # TYPE=\"radio\" VALUE=\"$setNumberHash{$sortedSetNumber}\"> ";
710 :     # }
711 :     #
712 :     # $problemDateLine .= "Problem Set Number $sortedSetNumber";
713 :     #
714 :     # ($timeNow < $odts ) && do {$problemDateLine .= &beforeOpenDateMsg($OpenDate);};
715 :     # ( $odts <= $timeNow ) && ($timeNow < $ddts) &&
716 :     # do {$problemDateLine .= &afterOpenDateMsg($DueDate);};
717 :     # ( $ddts <= $timeNow ) && ($timeNow < $adts) &&
718 :     # do {$problemDateLine .= &afterDueDateMsg($AnswerDate);};
719 :     # ( $adts <= $timeNow ) &&
720 :     # do {$problemDateLine .= &afterAnswerDateMsg;};
721 :     #
722 :     #
723 :     # push (@problemDates, $problemDateLine);
724 :     # }
725 :     #print join("\n\n", @problemDates),"\n"; # include open/due/answer dates
726 :     # # prepared above
727 :     #}
728 :    
729 :    
730 :     sub beforeOpenDateMsg {
731 :     my ($OpenDate) = @_;
732 :     my $out = " --- <em>Before open date</em> -- ";
733 :     $out .= "Open date is: $OpenDate <BR>";
734 :     $out;
735 :     };
736 :     sub afterOpenDateMsg { #and before Due Date
737 :     my ($DueDate) = @_;
738 :     my $out = " --- <em><B>OPEN</B></em>";
739 :     $out .= " -- Due date is: $DueDate <BR>";
740 :     $out;
741 :     };
742 :     sub afterDueDateMsg { #and before AnswerDate
743 :     my ($AnswerDate) = @_;
744 :     my $out = " --- <em><B>CLOSED</B></em> --";
745 :     $out .= " Answers available on: $AnswerDate <BR>";
746 :     $out;
747 :     };
748 :     sub afterAnswerDateMsg {
749 :     my $out = " --- <em><B>CLOSED</B></em> -- ";
750 :     $out .= " answers available.<BR>";
751 :     $out;
752 :     };
753 :    
754 :    
755 :     sub problemDates {
756 :     my ($OpenDate,$DueDate,$AnswerDate) = @_;
757 :     my $out = <<ENDproblemDatesHTML;
758 :     <PRE>
759 :     Open: $OpenDate
760 :     <B>Due: $DueDate</B>
761 :     Answer: $AnswerDate
762 :     </PRE>
763 :     ENDproblemDatesHTML
764 :    
765 :     $out;
766 :     }
767 :    
768 :     sub formatDateAndTime {
769 :     my ($timeStamp)=@_;
770 :     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
771 :     localtime($timeStamp);
772 :     my $twelveHour;
773 :     if($min<10){$min= "0" . $min;}
774 :    
775 :     if($hour==0){$twelveHour = 12 . ":" . $min . " AM";}
776 :     elsif($hour<12){$twelveHour= $hour . ":" . $min . " AM";}
777 :     elsif($hour==12){$twelveHour = $hour . ":" . $min . " PM";}
778 :     else {$twelveHour = ($hour-12) . ":" . $min . " PM";}
779 :    
780 :     if($year>99){$year = $year -100;}
781 :     if($year<10){$year= "0" . $year;}
782 :    
783 :     my $returnTimeString = ($mon+1) . "/" . $mday . "/" . $year . " at " . $twelveHour;
784 :     $returnTimeString;
785 :     }
786 :    
787 :    
788 :     sub unformatDateAndTime {
789 :     my ($string) = @_;
790 :     my $orgString =$string;
791 :     $string =~ s|^\s+||;
792 :     $string =~ s|\s+$||;
793 :     $string =~ s|at| at |i; ## OK if forget to enter spaces or use wrong case
794 :     $string =~ s|AM| AM|i; ## OK if forget to enter spaces or use wrong case
795 :     $string =~ s|PM| PM|i; ## OK if forget to enter spaces or use wrong case
796 :     $string =~ s|,| at |; ## start translating old form of date/time to new form
797 :    
798 :     my($date,$at,$time,$AMPM) = split(/\s+/,$string);
799 :     unless ($time =~ /:/) {
800 :     { ##bare block for 'case" structure
801 :     $time =~ /(\d\d)(\d\d)/;
802 :     my $tmp_hour = $1;
803 :     my $tmp_min = $2;
804 :     if ($tmp_hour eq '00') {$time = "12:$tmp_min"; $AMPM = 'AM';last;}
805 :     if ($tmp_hour eq '12') {$time = "12:$tmp_min"; $AMPM = 'PM';last;}
806 :     if ($tmp_hour < 12) {$time = "$tmp_hour:$tmp_min"; $AMPM = 'AM';last;}
807 :     if ($tmp_hour < 24) {
808 :     $tmp_hour = $tmp_hour - 12;
809 :     $time = "$tmp_hour:$tmp_min";
810 :     $AMPM = 'PM';
811 :     }
812 :     } ##end of bare block for 'case" structure
813 :    
814 :     }
815 :    
816 :     my ($mday, $mon, $year, $wday, $yday,$sec, $pm, $min, $hour);
817 :     $sec=0;
818 :     $time =~ /^([0-9]+)\s*\:\s*([0-9]*)/;
819 :     $min=$2;
820 :     $hour = $1;
821 :     if ( $hour < 1 or $hour > 12 or $min < 0 or $min > 59) {
822 :     &Global::error('File.pl: unformatDateAndTime error', "Incorrect date/time format $orgString. Correct format is 9/13/02 at 12:15 PM");
823 :     }
824 :     $pm = 0;
825 :     $pm = 12 if ($AMPM =~/PM/ and $hour < 12);
826 :     $hour += $pm;
827 :     $hour = 0 if ($AMPM =~/AM/ and $hour == 12);
828 :     $date =~ m!([0-9]+)\s*/\s*([0-9]+)/\s*([0-9]+)! ;
829 :     $mday =$2;
830 :     $mon=($1-1);
831 :     if ( $mday < 1 or $mday > 31 or $mon < 0 or $mon > 11) {
832 :     &Global::error('File.pl: unformatDateAndTime error', "Incorrect date/time format $orgString. Correct format is 9/13/02 at 12:15 PM");
833 :     }
834 :     $year=$3;
835 :     $wday="";
836 :     $yday="";
837 :     timelocal ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday);
838 :     }
839 :    
840 :     sub texInput
841 :    
842 :     ## Similar to the TeX input command. Takes a filename (with or without extension)
843 :     ## which is assumed to be in the \$templateDirectory.
844 :     ## E.G. print OUTFILE &texInput("file.tex");
845 :     ## or print OUTFILE &texInput("file");
846 :    
847 :     {
848 :     my $texInFile = $_[0];
849 :     my $texString;
850 :     if ($texInFile eq "") {
851 :     $texString = '';
852 :     } else {
853 :     unless ($texInFile =~ m#\.#) {$texInFile .= '.tex';}
854 :     open(TEX_IN_FILE,"${templateDirectory}$texInFile") ||
855 :     &Global::error("File.pl: textInput error", " Can't open ${templateDirectory}$texInFile");
856 :     my @texInputArray = <TEX_IN_FILE>;
857 :     close(TEX_IN_FILE);
858 :     $texString = join('',@texInputArray);
859 :     unless ($texString =~ /\n$/s) {$texString .= "\n";}
860 :     }
861 :     ## print "$texString";
862 :     $texString;
863 :     }
864 :    
865 :    
866 :    
867 :    
868 :    
869 :     # A very useful macro for making sure that all of the directories to a file have been constructed.
870 :    
871 :     sub surePathToTmpFile { # constructs intermediate directories if needed beginning at ${Global::htmlDirectory}tmp/
872 :     # the input path must be either the full path, or the path relative to this tmp sub directory
873 :     my $path = shift;
874 :     my $delim = &getDirDelim();
875 :     my $tmpDirectory = getCourseTempDirectory();
876 :     # if the path starts with $tmpDirectory (which is permitted but optional) remove this initial segment
877 :     $path =~ s|^$tmpDirectory|| if $path =~ m|^$tmpDirectory|;
878 :     $path = convertPath($path);
879 :     # find the nodes on the given path
880 :     my @nodes = split("$delim",$path);
881 :     # create new path
882 :     $path = convertPath("$tmpDirectory");
883 :    
884 :     while (@nodes>1 ) {
885 :     $path = convertPath($path . shift (@nodes) ."/");
886 :     unless (-e $path) {
887 :     # system("mkdir $path");
888 :     createDirectory($path,$Global::tmp_directory_permission, $Global::numericalGroupID) ||
889 :     wwerror($0, "Failed to create directory $path","","","");
890 :    
891 :     }
892 :    
893 :     }
894 :     $path = convertPath($path . shift(@nodes));
895 :    
896 :     # system(qq!echo "" > $path! );
897 :    
898 :     $path;
899 :    
900 :     }
901 :    
902 :    
903 :    
904 :    
905 :     sub fileFromPath {
906 :     my $path = shift;
907 :     my $delim =&getDirDelim();
908 :     $path = convertPath($path);
909 :     $path =~ m|([^$delim]+)$|;
910 :     $1;
911 :    
912 :     }
913 :    
914 :     sub directoryFromPath {
915 :     my $path = shift;
916 :     my $delim =&getDirDelim();
917 :     $path = convertPath($path);
918 :     $path =~ s|[^$delim]*$||;
919 :     $path;
920 :     }
921 :    
922 :     sub createDirectory
923 :     {
924 :     my ($dirName, $permission, $numgid) = @_;
925 :     mkdir($dirName, $permission) or
926 :     wwerror("$0: createDirectory error", " Can't do mkdir($dirName, $permission)");
927 :     chmod($permission, $dirName) or
928 :     wwerror("$0: createDirectory error", " Can't do chmod($permission, $dirName)");
929 :     unless ($numgid == -1) {chown(-1,$numgid,$dirName) or
930 :     wwerror("$0: createDirectory error", " Can't do chown(-1,$numgid,$dirName)");}
931 :     }
932 :     use Cwd;
933 :     sub createFile {
934 :     my ($fileName, $permission, $numgid) = @_;
935 :     # my $decimal_per = sprintf "%lo", $permission;
936 :     # print "\n IN createFile: file is $fileName, permission is $decimal_per, gid is $numgid\n";
937 :    
938 :     open(TEMPCREATEFILE, ">$fileName") ||
939 :     wwerror("File.pl: createFile error", " Can't open $fileName");
940 :     my @stat = stat TEMPCREATEFILE;
941 :     close(TEMPCREATEFILE);
942 :    
943 :     ## if the owner of the file is running this script (e.g. when the file is first created)
944 :     ## set the permissions and group correctly
945 :     if ($< == $stat[4]) {
946 :     # my $oldDirectory = cwd();
947 :     # warn " old directory is $oldDirectory<BR>\n";
948 :     # my $newDirectory = $fileName;
949 :     # $newDirectory =~ s|/[^/]+$||;
950 :     # warn " new directory is $newDirectory<BR>\n";
951 :     # $fileName =~ m|([^/]+$)|;
952 :     # my $newFileName = $1;
953 :     # warn "new File name = $newFileName<BR>\n";
954 :     # chdir $newDirectory;
955 :     # warn "changing to directory =" .cwd() ."<BR>\n";
956 :     #
957 :     #chmod(0777,$fileName);
958 :     my $tmp = chmod($permission,$fileName) or
959 :     warn("File.pl: createFile error", " Can't do chmod($permission, $fileName)");
960 :     chown(-1,$numgid,$fileName) or
961 :     warn("File.pl: createFile error", " Can't do chown($numgid, $fileName)");
962 :     # #warn "foo is readable<BR>\n" if -w 'foo.gif';
963 :     # #warn "chmod =" . chmod($permission,$newFileName) ||
964 :     # # warn("File.pl: createFile error", " Can't do chmod($permission, $newFileName)");
965 :     # #chdir $oldDirectory;
966 :     # #warn "changed back to directory =" .cwd() ."<BR>\n";
967 :     }
968 :     }
969 :    
970 :     sub rmDirectoryAndFiles
971 :     {
972 :     my ($PROBDIR) =@_;
973 :     my @allfiles = ();
974 :     opendir( DIRHANDLE, "$PROBDIR") || warn qq/Can't read directory $PROBDIR $!/;
975 :     @allfiles = map "$PROBDIR$_", grep( !/^\.\.?$/, readdir DIRHANDLE);
976 :     closedir(DIRHANDLE);
977 :     # print "unlinking<BR>",join("<BR>", @allfiles),"<P>";
978 :     unlink(@allfiles);
979 :     # print "removing directory $PROBDIR <P>";
980 :     rmdir("$PROBDIR");
981 :     }
982 :    
983 :    
984 :    
985 :     # this returns an array of set names sorted by due date (with all open sets first).
986 :     # It is called by a reference to a hash with keys the Set Names and values psvn's
987 :     # such as returned by &getAllProbSetNumbersHash or &getAllSetNumbersForStudentLoginHash
988 :    
989 :     sub sortSetNamesByDueDate {
990 :     my ($setNameHashref) = @_;
991 :     my %setNameHash = %$setNameHashref;
992 :     my ($setName,$psvn,$ddts,$timeNow);
993 :     my %dueTimes =();
994 :    
995 :     foreach $setName (keys %setNameHash) {
996 :     $psvn=$setNameHash{$setName};
997 :     &attachProbSetRecord($psvn);
998 :     $ddts=&getDueDate($psvn);
999 :     $dueTimes{$setName} = $ddts;
1000 :     }
1001 :    
1002 :     my @sortedSetNames = sort
1003 :    
1004 :     ## Sort setnumbers by due date. Using an anonymous block so that
1005 :     ## dueTimes gets passes without making it global to FILE.pl or
1006 :     ## passing it to a sorting subroutine (can we pass this?)
1007 :    
1008 :     {
1009 :     $timeNow = time;
1010 :     if ( ($dueTimes{$a} <= $timeNow) and ($dueTimes{$b} <= $timeNow) )
1011 :     {
1012 :     $dueTimes{$a} <=> $dueTimes{$b}
1013 :     or
1014 :     $a cmp $b
1015 :     }
1016 :     elsif ( ($dueTimes{$a} > $timeNow) and ($dueTimes{$b} > $timeNow) )
1017 :     {
1018 :     $dueTimes{$a} <=> $dueTimes{$b}
1019 :     or
1020 :     $a cmp $b
1021 :     }
1022 :     else
1023 :     {
1024 :     $dueTimes{$b} <=> $dueTimes{$a}
1025 :     }
1026 :     }
1027 :    
1028 :     keys %setNameHash ;
1029 :     @sortedSetNames;
1030 :     }
1031 :    
1032 :     sub checkClasslistFile {
1033 :     ## takes as parameters the number of fields and the full path name of
1034 :     ## the classlist file. Checks that the file iv valid, i.e. (1) all records
1035 :     ## have the same number of fields, (2) the first fields (studentID's, usually SS#'s) are
1036 :     ## all distinct and (3) the last fields (the loginID's) are all distinct,
1037 :     ## and (4) that studentID's and loginID's comtain only valid characters
1038 :    
1039 :     my($noOfFields,$fileName)=@_;
1040 :     my $msg = htmlCheckClasslistFile($noOfFields,$fileName);
1041 :     unless ($msg eq 'OK') {
1042 :     &wwerror("$0","$msg");
1043 :     }
1044 :     }
1045 :    
1046 :     sub htmlCheckClasslistFile {
1047 :     ## takes as parameters the number of fields and the full path name of
1048 :     ## the classlist file. Checks that the file iv valid, i.e. (1) all records
1049 :     ## have the same number of fields, (2) the first fields (studentID's, usually SS#'s) are
1050 :     ## all distinct and (3) the last fields (the loginID's) are all distinct,
1051 :     ## and (4) that studentID's and loginID's comtain only valid characters and
1052 :     ## (5) that other fields do not contain bas chacters
1053 :    
1054 :     my($noOfFields,$fileName)=@_;
1055 :    
1056 :     open (FILE, "$fileName") or
1057 :     &wwerror("$0","can't open $fileName");
1058 :     my @classList = <FILE>;
1059 :     close(FILE);
1060 :    
1061 :     my $msg = checkClasslistArray($noOfFields, \@classList,$fileName);
1062 :     return $msg;
1063 :     }
1064 :    
1065 :     sub checkClasslistArray {
1066 :     ## takes as parameters the number of fields and a ref to
1067 :     ## the classlist array. Checks that the file iv valid, i.e. (1) all records
1068 :     ## have the same number of fields, (2) the first fields (studentID's, usually SS#'s) are
1069 :     ## all distinct and (3) the last fields (the loginID's) are all distinct,
1070 :     ## and (4) that studentID's and loginID's comtain only valid characters and
1071 :     ## (5) that other fields do not contain bas chacters
1072 :    
1073 :     my($noOfFields,$classListref,$fileName)=@_;
1074 :     my($noOfDelim,$dbString,$num,$i,@classList);
1075 :     my(@keyList);
1076 :     my $msg ='';
1077 :     $noOfDelim = $noOfFields -1;
1078 :    
1079 :     @classList = @$classListref;
1080 :    
1081 :     foreach $dbString (@classList) {
1082 :     unless ($dbString =~ /\S/) {next;}
1083 :     chomp $dbString;
1084 :     $num=($dbString =~s/$DELIM/$DELIM/g);
1085 :     if ($num != $noOfDelim) {
1086 :     $num =$num+1;
1087 :     $msg = "\n\n The classlist file\n $fileName \n is corrupted. The record\n
1088 :     $dbString \n contains $num fields instead of $noOfFields fields. \nYou
1089 :     must correct this and then run this script again.
1090 :     \n\n";
1091 :     return $msg;
1092 :     }
1093 :     }
1094 :     my (@SSList, @loginList);
1095 :     @SSList=(); @loginList=();
1096 :     foreach $dbString (@classList) {
1097 :     unless ($dbString =~ /\S/) {next;}
1098 :     chomp $dbString;
1099 :     my @classListRecord=&getRecord($dbString);
1100 :     my ($studentID, $lastName, $firstName, $status, $comment, $section,$recitation, $email_address, $login_name)
1101 :     = @classListRecord;
1102 :     # next if &dropStatus($status); ## ignore students who have dropped
1103 :     unless ($studentID =~ /^[\w\-\.]+$/) {
1104 :     $msg = "\n\n The classlist file\n$fileName\n\n is corrupted. The record
1105 :     \n$dbString \n contains the invalid studentID: $studentID
1106 :     \n studentID's can contain only upper and lower case letters, digits, -, dot('.'), and _
1107 :     \n You must correct this and then run this script again.\n\n";
1108 :     return $msg;
1109 :     }
1110 :     unless ($login_name =~ /^[\w\-\.]+$/) {
1111 :     $msg = "\n\n The classlist file\n$fileName\n\n is corrupted. The record
1112 :     \n$dbString \n contains the invalid loginName: $login_name
1113 :     \n loginName's can contain only upper and lower case letters, digits, -, dot('.'), and _
1114 :     \n You must correct this and then run this script again.\n\n";
1115 :     return $msg;
1116 :     }
1117 :    
1118 :     ## test entries for bad characters.
1119 :     my @entries = ($lastName, $firstName, $status, $comment, $section,$recitation, $email_address);
1120 :     my $item ='';
1121 :     foreach $item (@entries) {
1122 :     my $msg = test_entry($item);
1123 :     unless ($msg eq 'OK') {return $msg;}
1124 :     }
1125 :    
1126 :     push(@SSList,$studentID);
1127 :     push(@loginList,$login_name);
1128 :     }
1129 :     @SSList = sort(@SSList);
1130 :     for ($i=0; $i < @SSList-1; $i++) {
1131 :     if ($SSList[$i] eq $SSList[$i+1]) {
1132 :     $msg = "\n\n The classlist file\n$fileName\n\n is corrupted. Duplicate studentID's equal
1133 :     to $SSList[$i] in\n $fileName\nYou must correct this and then run this script again.\n\n";
1134 :     return $msg;
1135 :     }
1136 :     }
1137 :     @loginList = sort(@loginList);
1138 :     for ($i=0; $i < @loginList-1; $i++) {
1139 :     if ($loginList[$i] eq $loginList[$i+1]) {
1140 :     $msg ="\n\n The classlist file\n$fileName\n\n is corrupted. Duplicate loginNames equal
1141 :     to $loginList[$i] in\n
1142 :     $fileName\nYou must correct this and then run this script again.\n\n";
1143 :     return $msg;
1144 :     }
1145 :     }
1146 :     $msg ='OK';
1147 :     return $msg;
1148 :     }
1149 :    
1150 :     ### macros for writing and reading html tables
1151 :    
1152 :     sub array2htmlRow {
1153 :     ## The parameter is an array whose entries will beccome elements of a row
1154 :     ## in an html table. The output is a string formated the same way Excel
1155 :     ## formats html tables: numbers aligned right, other things left.
1156 :    
1157 :     my @inArray = @_;
1158 :     my ($item,$align);
1159 :     my $outString ='';
1160 :     $outString = join '','<TR ALIGN="left" VALIGN="bottom">',"\n";
1161 :     foreach $item (@inArray) {
1162 :     unless ($item =~ /\S/) {$item = '&nbsp;';}
1163 :     if ($item =~/^[\d\.]+$/){$align ='RIGHT'} else {$align ='LEFT'}
1164 :     $outString .= join '','<TD ALIGN=',$align,'>',"\n";
1165 :     $outString .= join '',$item,'</TD>',"\n";
1166 :     }
1167 :     $outString .= join '','</TR>',"\n";
1168 :     }
1169 :    
1170 :     sub array2htmlRowForm {
1171 :     ## The parameter is an array whose first entry is the row number (1, 2, etc)
1172 :     ## and remaining entries will become elements of a row
1173 :     ## in an html table.
1174 :    
1175 :     my ($row,@inArray) = @_;
1176 :     my ($item,$size);
1177 :     my $outString ='';
1178 :     my $col =1;
1179 :     $outString = join '','<TR ALIGN=LEFT VALIGN=BOTTOM>',"\n";
1180 :     foreach $item (@inArray) {
1181 :     unless ($item =~ /\S/) {$item = ' ';}
1182 :     $size = length($item);
1183 :     $outString .= join '','<TD>',"\n";
1184 :     $outString .= join '','<INPUT TYPE="TEXT" SIZE = ', $size, ' NAME="',"row${row}col$col",'" VALUE="',"$item",'"> </TD>',"\n";
1185 :    
1186 :     $col++;
1187 :     }
1188 :     $outString .= join '','</TR>',"\n";
1189 :     }
1190 :    
1191 :     sub delimitedArray2htmlTable {
1192 :    
1193 :     # Takes a ref to an array whose elements are rows of a delimited file
1194 :     # and outputs a string containing
1195 :     # an html table version of the array suitable for viewing and editing
1196 :     # in Excel or a browser such as Netscape/Communicator. If the second
1197 :     # optional parameter is 'htmlform', the output is an html form. Otherwise
1198 :     # the output is a plain html document.
1199 :     # Blank lines are skipped. White space is removed.
1200 :    
1201 :     my ($inArrayref, $type) = @_;
1202 :    
1203 :     ## setup html header and initial table stuff
1204 :     my $rowString;
1205 :     my $outString = "<Table border>\n";
1206 :    
1207 :     ## translate data from delimited format to html format
1208 :     my $row =1;
1209 :     foreach (@$inArrayref)
1210 :     {
1211 :     unless ($_ =~ /\S/) {next;} ## skip blank lines
1212 :     chomp;
1213 :     if ( (defined $type) and ($type eq 'htmlform')) {$rowString = &array2htmlRowForm($row, &getRecord($_));}
1214 :     else {$rowString = &array2htmlRow(&getRecord($_));}
1215 :     $outString .= $rowString;
1216 :     $row++;
1217 :     }
1218 :    
1219 :     ## setup html end table
1220 :     $outString .= join '','</Table>',"\n" ;
1221 :     }
1222 :    
1223 :     sub delimitedArray2html {
1224 :    
1225 :     # Takes a ref to an array whose elements are rows of a delimited file
1226 :     # and outputs a string containing
1227 :     # an html version of the array suitable for viewing and editing
1228 :     # in Excel or a browser such as Netscape/Communicator. The $label is the name
1229 :     # appearing at the top of the form or page. If the third
1230 :     # optional parameter is 'htmlform', the output is an html form. Otherwise
1231 :     # the output is a plain html document.
1232 :     # Blank lines are skipped. White space is removed.
1233 :    
1234 :     my ($inArrayref, $label, $type) = @_;
1235 :    
1236 :     ## setup html header and initial table stuff
1237 :     my $rowString;
1238 :     my $outString = join '','<HTML>',"\n" ,'<HEAD>',"\n", '<TITLE>';
1239 :     $outString .= join '',$label,'</TITLE>',"\n",'</HEAD>',"\n",'<BODY>',"\n";
1240 :     $outString .= join '','<H1><CENTER>',$label,'</CENTER></H1>',"\n";
1241 :     $outString .= &delimitedArray2htmlTable($inArrayref, $type);
1242 :    
1243 :     ## setup html footer stuff
1244 :     $outString .= join '','</BODY>',"\n", '</HTML>';
1245 :     }
1246 :    
1247 :    
1248 :     sub delim2html {
1249 :    
1250 :     # Takes a delimited file name as input and outputs a string containing
1251 :     # an html version of the input file suitable for viewing and editing
1252 :     # in Excel or a browser such as Netscape/Communicator. If the second
1253 :     # optional parameter is 'htmlform', the output is an html form. Otherwise
1254 :     # the output is a plain html document.
1255 :     # Blank lines are skipped. White space is removed.
1256 :    
1257 :     my ($inFileName,$type) = @_;
1258 :    
1259 :     my $shortFileName = $inFileName;
1260 :     unless (defined($type) and $type eq 'htmlform') {$type = 'html';}
1261 :     if ($shortFileName =~ m|$dd|) {
1262 :     $shortFileName =~ m|$dd([^$dd]*)$|; ## extract filename from full path name
1263 :     $shortFileName = $1;
1264 :     }
1265 :     $shortFileName =~ s|\..*||; ## remove extension
1266 :     open(INFILE, $inFileName) || wwerror("$0", "can't open $inFileName");
1267 :     my @outArray = <INFILE>;
1268 :     close(INFILE);
1269 :     my $outString = delimitedArray2html(\@outArray,$shortFileName,$type);
1270 :     $outString;
1271 :     }
1272 :    
1273 :    
1274 :    
1275 :    
1276 :     sub htmlPage2htmlTable { ## Takes a string which contains a full html page
1277 :     ## containing a single table and removes all the
1278 :     ## header and footer material leaving only the row
1279 :     ## entries between <table> and </table>. Also removes all the
1280 :     ## <font ...> and </font> stuff from within the table.
1281 :     ## The cleaned up string is returned.
1282 :    
1283 :     my ($inString) = @_;
1284 :     $inString =~ s|^.*<\s*table.*?>||is; ## remove <table> and stuff before
1285 :     $inString =~ s|<\s*/table\s*>.*?$||is; ## remove </table> and stuff after
1286 :     $inString =~ s|<\s*/*font.*?>||gis; ## remove font stuff
1287 :     $inString =~ s|>[^>]*$|>|s; ## remove any stuff after last >
1288 :     $inString;
1289 :     }
1290 :    
1291 :     sub htmlTable2delim { ## Takes a string (e.g. output from htmlPage2htmlTable) which
1292 :     ## contains the rows from an html table and returns a string
1293 :     ## containing the table data in delimited format.
1294 :    
1295 :     my ($inString) = @_;
1296 :     my ($outString, $item, $rowString);
1297 :     $outString ='';
1298 :     while ($inString){
1299 :     $inString =~ s|^(.*?<\s*/tr\s*>)||is; # get next row
1300 :     $item = $1;
1301 :     $rowString = join("${DELIM}",&htmlRow2array($item));
1302 :     $outString .= join '', $rowString, " \n";
1303 :     }
1304 :     $outString;
1305 :     }
1306 :    
1307 :     sub htmlForm2delim { ## Takes a reference to the associtive array of inputs from
1308 :     ## a form. The $inputs{row5col8} is the element for the 5th row
1309 :     ## and 8 column. It is assumed the input is a rectangular array
1310 :     ##Returns a string containing the table data in delimited format.
1311 :    
1312 :     my ($inputsref) = @_;
1313 :     my %inputs = %$inputsref;
1314 :     my ($item, $index,$row,$col);
1315 :     my $maxCol = 1;
1316 :     my $maxRow = 1;
1317 :     my @rowColIndex = grep /^row\d+col\d+$/, keys %inputs;
1318 :     foreach $index (@rowColIndex) {
1319 :     $index =~ /^row(\d+)col(\d+)$/;
1320 :     if ($1 > $maxRow) {$maxRow = $1};
1321 :     if ($2 > $maxCol) {$maxCol = $2};
1322 :     }
1323 :    
1324 :     my @outArray =();
1325 :     my $rowString ='';
1326 :     my @rowArray= ();
1327 :    
1328 :     for $row (1..$maxRow) {
1329 :     @rowArray= ();
1330 :     for $col (1..$maxCol) {push @rowArray, $inputs{"row${row}col${col}"};}
1331 :     $rowString = join("${DELIM}",@rowArray);
1332 :     push (@outArray,$rowString);
1333 :     }
1334 :     @outArray = &columnArrayArrange(@outArray); ## line up columns
1335 :     my $outString = join('',@outArray);
1336 :     $outString;
1337 :     }
1338 :    
1339 :    
1340 :    
1341 :     sub htmlRow2array {
1342 :     ## The parameter is a string "<TR ... /TR>" containing one row
1343 :     ## in an html table. The output is an array containing the entries
1344 :     ## contained in that row.
1345 :    
1346 :     my ($inString) = @_;
1347 :     $inString =~ s|^.*<\s*tr.*?>||is; ## remove <tr> and stuff before
1348 :     $inString =~ s|<\s*/tr\s*>.*$||is; ## remove </tr> and stuff after
1349 :     $inString =~ s|>[^>]*$|>|s; ## remove any stuff after last >
1350 :    
1351 :     my @outArray =();
1352 :     my $item;
1353 :     while ($inString){
1354 :     $inString =~ s%^(.*?<)\s*/t[d|h]\s*>%%is; # get next entry
1355 :     $item = $1;
1356 :     $item =~ m|>\s*(.*?)<|is; # get entry
1357 :     $item =$1;
1358 :     $item =~ s|\s*$||; # remove trailing spaces
1359 :     if (($item eq '&nbsp;') or ($item eq '')) {$item =' '}
1360 :     push @outArray, $item;
1361 :     }
1362 :     @outArray;
1363 :     }
1364 :    
1365 :    
1366 :     ## this subroutine prints all environment variables.
1367 :     ## adapted from http://www.cgi-resources.com/Documentation/Environment_Variables/
1368 :     ## takes parameters html_top, html_bot which print html top and bottom matter if set
1369 :     sub printEnvVars {
1370 :    
1371 :     my ($top, $bot) = @_;
1372 :     my ($bigcontent, @content, $content,$name,$value,%input,$tvar,$key);
1373 :     # First, if METHOD=GET we grab the environment variable
1374 :     # containing the Query_String - otherwise we grab the
1375 :     # environment variable Content_Length.
1376 :     if ($ENV{'REQUEST_METHOD'} eq "GET") {
1377 :     $bigcontent = $ENV{'QUERY_STRING'};
1378 :     } # Close if bracket
1379 :     else {
1380 :     read(STDIN, $bigcontent, $ENV{'CONTENT_LENGTH'});
1381 :     } # Close else bracket
1382 :     # bigcontent now contains a long string which is broken by
1383 :     # ampersands between the various form elements. So let's split
1384 :     # it and load it into an array
1385 :     @content = split(/&/, $bigcontent);
1386 :     # But we aren't done yet. All of the spaces in the form data
1387 :     # were replaced by pluses. Other non-alpha characters except
1388 :     # equal signs were replaced by their hex values. So now we
1389 :     # need to step through the array and translate them back into
1390 :     # their "sent" form.
1391 :     foreach $content (@content) {
1392 :     # Split HTML form's "NAME" and "VALUE" at equal signs
1393 :     ($name, $value) = split(/=/, $content);
1394 :     # Replace the pluses with spaces
1395 :     $value =~ tr/+/ /;
1396 :     # Translate the hex (now preceded by percent sign) into ASCII
1397 :     $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
1398 :     # And finish by loading input variables for use in program.
1399 :     # You call it by $input{'formvarname'} to get the literal
1400 :     # that the user typed into that field on the Form.
1401 :     $input{$name} = $value;
1402 :     } # Close bracket for foreach loop
1403 :    
1404 :     # Tell the server that we are going to send it to user's browser
1405 :     if ($top eq 'html_top') {print "Content-type: text/html\n\n";
1406 :     # So we don't have to type backslashes everywhere before reserved
1407 :     # characters in the HTML, we use this so the PERL compiler will
1408 :     # know that what follows is literal (except for variable names)
1409 :     # But be careful - still need backslash in front of literal at
1410 :     # signs, dollar signs, etc., since PERL assumes a variable name
1411 :     # follows these characters.
1412 :     print <<ENDOFTEXT;
1413 :     <HTML><HEAD><TITLE>Environment Variable
1414 :     Test</TITLE></HEAD>
1415 :     <BODY BGCOLOR="#FFFFFF">
1416 :    
1417 :     ENDOFTEXT
1418 :     }
1419 :     # Now, simply sort and print the names and values of each of the
1420 :     # environment variables from the keyed array to browser window
1421 :     foreach $key (sort keys(%ENV)) {print
1422 :     "<B>$key:<\/B>$ENV{$key}<BR>";}
1423 :    
1424 :     if ($bot eq 'html_bot') {
1425 :     print <<ENDOFTEXT;
1426 :    
1427 :     <P>
1428 :     </BODY>
1429 :     </HTML>
1430 :     ENDOFTEXT
1431 :    
1432 :     }
1433 :     }
1434 :    
1435 :     sub backupFile {
1436 :     ## takes as a parameter the full filename
1437 :     ## makes upto three backups of file with x, y, or z appended to filename where x
1438 :     ## the most recent backup
1439 :    
1440 :     my $fileName =$_[0];
1441 :     my $orgFileName = "$fileName";
1442 :     my ($ext, $fnMinusExt,$noPeriod);
1443 :     if (! ($orgFileName =~ m|\.|)) {
1444 :     $noPeriod =1;
1445 :     $fnMinusExt = $orgFileName;
1446 :     $ext ='';
1447 :     }
1448 :     else {
1449 :     $noPeriod =0;
1450 :     $orgFileName =~ m|^(.*)\.([^\.]*)$|;
1451 :     $fnMinusExt = $1;
1452 :     $ext = $2;
1453 :     }
1454 :     my $period = '.';
1455 :     $period = '' if $noPeriod;
1456 :     if (-e "${fnMinusExt}y${period}${ext}") {
1457 :     rename("${fnMinusExt}y${period}$ext","${fnMinusExt}z${period}$ext") or
1458 :     &wwerror("$0","can't rename ${fnMinusExt}y${period}$ext");
1459 :     }
1460 :    
1461 :     if (-e "${fnMinusExt}x${period}$ext") {
1462 :     rename("${fnMinusExt}x${period}$ext","${fnMinusExt}y${period}$ext") or
1463 :     &wwerror("$0","can't rename ${fnMinusExt}x${period}$ext");
1464 :     }
1465 :    
1466 :     if (-e "${fnMinusExt}${period}$ext") {
1467 :     rename("${fnMinusExt}${period}$ext","${fnMinusExt}x${period}$ext") or
1468 :     &wwerror("$0","can't rename ${fnMinusExt}${period}$ext");
1469 :     }
1470 :     }
1471 :    
1472 :     sub stripWhiteSpace { ## strip initial and trailing whitespace
1473 :     my $string = $_[0];
1474 :     $string =~ s/\s*$//; # remove trailing whitespace
1475 :     $string =~ s/^\s*//; # remove initial spaces
1476 :     $string;
1477 :     }
1478 :    
1479 :     sub test_entry{ ## check for bad characters. & and = are used as delimiters
1480 :     ## in databases. DELIM (usually a coma) is used in csv files
1481 :     my $entry = shift;
1482 :     my $msg = 'OK';
1483 :     if ($entry =~ /[=&$DELIM]/) {
1484 :     $msg = " The entry: $entry is invalid.
1485 :     An entry can not contain any of the following characters: $DELIM & =
1486 :     You must go back and correct this.\n";
1487 :     }
1488 :     $msg;
1489 :     }
1490 :    
1491 :     sub testNewStudentLogin {
1492 :     my $login_name = shift;
1493 :     my $newStudentID = shift;
1494 :     my $msg = 'OK';
1495 :     unless ($login_name =~ /^[\w\-\.]+$/) {
1496 :     $msg = " The login name: $login_name is invalid.
1497 :     Login name's can contain only upper and lower case letters, digits, -, dot('.'), and _
1498 :     you must go back and correct this.\n";
1499 :     return $msg;
1500 :     }
1501 :     my %currentLogins = %{getLoginName_StudentID_Hash()};
1502 :     if (defined $currentLogins{$login_name}){
1503 :     attachCLRecord($login_name);
1504 :    
1505 :     my $studentLastName = CL_getStudentLastName($login_name);
1506 :     my $studentFirstName = CL_getStudentFirstName($login_name);
1507 :     my $studentID = CL_getStudentID($login_name);
1508 :    
1509 :     $msg = " The login name: $login_name is already in use.
1510 :     It is assigned to $studentFirstName $studentLastName ($studentID).
1511 :     You must go back and choose a login name which is not yet being used.\n";
1512 :     return $msg;
1513 :     }
1514 :    
1515 :     ## check that if student login exists in webwork database, the studentID's match
1516 :    
1517 :     if ( -e "${databaseDirectory}$Global::database" ){
1518 :     my %loginName_StudentID_Hash_from_WW_DB =%{getLoginName_StudentID_Hash_from_WW_DB()};
1519 :     if (defined $loginName_StudentID_Hash_from_WW_DB{$login_name}) {
1520 :     my $oldStudentID = $loginName_StudentID_Hash_from_WW_DB{$login_name};
1521 :     unless ($newStudentID eq $oldStudentID) {
1522 :     my %setNumberHash = &getAllSetNumbersForStudentLoginHash($login_name);
1523 :     my @SetNumberKeys = keys(%setNumberHash);
1524 :     $msg = " The login name: $login_name is already in use in the webwork problem database.
1525 :     However, the new student ID ($newStudentID) does not match the old student ID ($oldStudentID).
1526 :     The following problem sets exist for $login_name $oldStudentID:
1527 :     Sets: @SetNumberKeys
1528 :     You have three choices.
1529 :     (1) Go back and use $oldStudentID for the student ID in which case the above sets will again be
1530 :     assigned to $login_name $oldStudentID.
1531 :     (2) Go back and choose a login name which is not yet being used.
1532 :     (3) Delete the problem sets listed above for $login_name $oldStudentID and then try again adding the student
1533 :     $login_name $newStudentID.";
1534 :    
1535 :     return $msg;
1536 :     }
1537 :     }
1538 :     }
1539 :     $msg;
1540 :     }
1541 :    
1542 :    
1543 :     sub testNewStudentID {
1544 :     my $studentID = shift;
1545 :     my $newLogin_name = shift;
1546 :     my $msg ='OK';
1547 :     unless ($studentID =~ /^[\w\-\.]+$/) {
1548 :     $msg = " The student ID: $studentID is invalid.
1549 :     student ID's can contain only upper and lower case letters, digits, -, dot('.'), and _
1550 :     you must go back and correct this.\n";
1551 :     return ($msg);
1552 :     }
1553 :     my %currentIDs = %{getStudentID_LoginName_Hash()};
1554 :    
1555 :     if (defined $currentIDs{$studentID}) {
1556 :     my $oldLogin = $currentIDs{$studentID};
1557 :     attachCLRecord($oldLogin);
1558 :    
1559 :     my $studentLastName = CL_getStudentLastName($oldLogin);
1560 :     my $studentFirstName = CL_getStudentFirstName($oldLogin);
1561 :    
1562 :    
1563 :     $msg = " The student ID: $studentID is already in use.
1564 :     It is assigned to $studentFirstName $studentLastName ($oldLogin).
1565 :     you must go back and choose a student ID which is not yet being used.\n";
1566 :     return $msg;
1567 :     }
1568 :    
1569 :     ## check that if student ID exists in webwork database, the student login's match
1570 :    
1571 :     if ( -e "${databaseDirectory}$Global::database" ){
1572 :     my %loginName_StudentID_Hash_from_WW_DB =%{getLoginName_StudentID_Hash_from_WW_DB()};
1573 :     my %studentID_LoginName_Hash_from_WW_DB = reverse %loginName_StudentID_Hash_from_WW_DB;
1574 :     if (defined $studentID_LoginName_Hash_from_WW_DB{$studentID}) {
1575 :     my $oldLogin_name = $studentID_LoginName_Hash_from_WW_DB{$studentID};
1576 :     unless ($newLogin_name eq $oldLogin_name) {
1577 :     my %setNumberHash = &getAllSetNumbersForStudentLoginHash($oldLogin_name);
1578 :     my @SetNumberKeys = keys(%setNumberHash);
1579 :     $msg = " The student ID: $studentID is already in use in the webwork problem database.
1580 :     However, the new student Login name ($newLogin_name) does not match the old student Login name ($oldLogin_name).
1581 :     The following problem sets exist for $oldLogin_name $studentID:
1582 :     Sets: @SetNumberKeys
1583 :     You have three choices.
1584 :     (1) Go back and use $oldLogin_name for the student login name in which case the above sets will again be
1585 :     assigned to $oldLogin_name $studentID.
1586 :     (2) Go back and choose a student ID which is not yet being used.
1587 :     (3) Delete the problem sets listed above for $oldLogin_name $studentID and then try again adding the student
1588 :     $newLogin_name $studentID.";
1589 :    
1590 :     return $msg;
1591 :     }
1592 :     }
1593 :     }
1594 :    
1595 :    
1596 :     $msg;
1597 :     }
1598 :    
1599 :     sub getClasslistFilesAndLabels { ## returns a two element array
1600 :     ## the 0th element is a ref to an array of files
1601 :     ## the 1st element is a ref to a hash of labels
1602 :     my $Course = shift;
1603 :     my $defaultClasslistFile = getCourseClasslistFile($Course);
1604 :    
1605 :     ## find the available files
1606 :    
1607 :     opendir CLASSLISTDIR, $templateDirectory or wweror($0,"Can't open directory $templateDirectory");
1608 :     my @allFiles = grep !/^\./, readdir CLASSLISTDIR;
1609 :     closedir CLASSLISTDIR;
1610 :    
1611 :     ## sort the files
1612 :    
1613 :     my @classlistFiles = grep /\.lst$/,@allFiles;
1614 :     my @sortedNames = sort @classlistFiles;
1615 :    
1616 :     ## put the default classlist file first if it exists
1617 :     my $shortFileName = $defaultClasslistFile;
1618 :     if ($shortFileName =~ m|$dd|) {
1619 :     $shortFileName =~ m|$dd([^$dd]*)$|; ## extract filename from full path name
1620 :     $shortFileName = $1;
1621 :     }
1622 :     my @newSortedNames = grep !/^$shortFileName$/, @sortedNames;
1623 :     if ($#newSortedNames != $#sortedNames) {
1624 :     unshift @newSortedNames,$shortFileName;
1625 :     @sortedNames = @newSortedNames;
1626 :     }
1627 :    
1628 :     ## generate labels
1629 :     my %label_hash = ();
1630 :    
1631 :     my ($ind,$date,$fileName,@stat);
1632 :     for $ind (@sortedNames) {
1633 :     $fileName = "${templateDirectory}$ind";
1634 :     if (-e $fileName) {
1635 :     @stat = stat($fileName);
1636 :     $date = $stat[9];
1637 :     $date = formatDateAndTime($date);
1638 :     $date =~ s|\s*at.*||;
1639 :     $label_hash{$ind} = "$ind --- Last Changed $date";
1640 :     }
1641 :     }
1642 :     (\@sortedNames,\%label_hash);
1643 :     }
1644 :    
1645 :    
1646 :    
1647 :    
1648 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9