#!/usr/local/bin/webwork-perl

## $Id$


####################################################################
# Copyright @ 1995-1998 University of Rochester
# All Rights Reserved
####################################################################



# This file is proceduresForBuildProbSetDB.pl
# Call with command arguments of the form:  set5.def  classl.gp
# This file loads the database from the class list and the set definition
###################################################################

use strict;

#$method ='readFromLogFile';  ## or 'createNewPSVNs'
#$method ='createNewPSVNs';
#$logFileName = "sampleCourse_set:3_bak3.psvnlog";


#########################

sub buildProbSetDB  ## builds the problem set and returns a message
	{
	my ($classID,$setDefFilename,$method,$logFileName,$outputFormat) = @_;
	my $scriptDirectory   = $Global::scriptDirectory;
	&Global::getCourseEnvironment("$classID");
	my $databaseDirectory = $Global::databaseDirectory;
	my $templateDirectory = $Global::templateDirectory;
	my $logsDirectory = getCourseLogsDirectory();

	require "${scriptDirectory}$Global::DBglue_pl";
	require "${scriptDirectory}$Global::classlist_DBglue_pl";
	require "${scriptDirectory}$Global::FILE_pl";


			 # Get data from set definition

	my $fileName="${templateDirectory}$setDefFilename";  ## e.g. $fileName=set5.def
	my $PIN;
	my $message = '';
	$message .= "\nGetting set definition from file: $fileName\n";
   # print  "Getting set definition from file: $fileName\n";

	my ($setNumber,$setHeaderFileName,$probHeaderFileName,$dueDate,$openDate,$answerDate,$problemListref,$problemValueListref,$problemAttemptLimitListref)
		= &readSetDef($fileName);
	my @problemList = @$problemListref;
	my @problemValueList = @$problemValueListref;
	my @problemAttemptLimitList = @$problemAttemptLimitListref;
	$message .= "  set Name or Number: $setNumber\n  openDate: $openDate\n  dueDate: $dueDate\n  answerDate: $answerDate\n\n";


	## put dates into standard perl format, i.e. seconds since 1980

	$openDate = &unformatDateAndTime($openDate);
	$dueDate = &unformatDateAndTime($dueDate);
	$answerDate = &unformatDateAndTime($answerDate);


	#get data from class list.
  #  $fileName="${templateDirectory}$classlistFilename";  ## e.g. fileName=m161.lst
  #  print "Getting class list from $fileName\n";
# checkClasslistFile($Global::noOfFieldsInClasslist,$fileName);
 #   open(FILE, "$fileName") || wwerror($0, "Can't open $fileName");
 #   my @classList=<FILE>;
 #   close(FILE);
	###################################
	#  Before building the database we pause to check that the database file exists
	# (if not we create it).
	###################################
	if ( -e "${databaseDirectory}$Global::database" ) {

	my @data = stat("${databaseDirectory}$Global::database");
	## This statement is a hack.  When using Berkeley DB 1.85 on a FreeBSD system
	## sometimes this test returns true even when the database file does not exist.
	## We have never seen this happen with the stat statement. Very strange indeed.

 #   $message .= "data base file statistics: @data\n\n";

	$message .= "Loading WeBWorK database: ${databaseDirectory}$Global::database\n";
	}
	else {
	   # print "Data base ${databaseDirectory}$Global::database does not exist.\n\n";
		 $message .=   "Data base ${databaseDirectory}$Global::database does not exist.\n\n";
 #       print "Create new database?(y or n)";
 #       my $tempinput = <STDIN>;
		my $tempinput = 'y';       ## always create a new database
		if ($tempinput =~ /y|Y/) {
	   #     print "Creating new data base ${databaseDirectory}$Global::database.\n";
			$message .= "Creating new data base ${databaseDirectory}$Global::database.\n";
			create_db("${databaseDirectory}$Global::database", $Global::webwork_database_permission);
			if ( -e "${databaseDirectory}$Global::database" ) {
			chmod($Global::webwork_database_permission,"${databaseDirectory}$Global::database") ||
							 wwerror($0, "Can't do chmod($Global::webwork_database_permission,${databaseDirectory}$Global::database)");
			chown(-1,$Global::numericalGroupID,"${databaseDirectory}$Global::database")  ||
							 wwerror($0,"Can't do chown(-1,$Global::numericalGroupID,${databaseDirectory}$Global::database)");
		   # print "New data base created\n";
			$message .=   "New data base created\n";
			}
			else {
			 #   print "New data base could not be created\n";
				$message .=   "New data base could not be created\n";
			}
		}
		else {

			print "Abort buildProbSetDB.pl\n\n";
			exit;
			}
		}

	## Create a logfile in which we will save the following data for each new psvn created
	## $login_name $PIN seedForProb1 seedForProb2 seedForProb3 ...

	##don't overwrite existing backups
	my $i=1;
	while(-e "${logsDirectory}${classID}_set:${setNumber}_bak${i}.psvnlog") {$i++;}
	my $fullLogFileName ="${logsDirectory}${classID}_set:${setNumber}_bak${i}.psvnlog";
	&createFile($fullLogFileName, 0660, $Global::numericalGroupID);
	open(LOGFILE,">$fullLogFileName")  or  wwerror($0, "Can't open $fullLogFileName");

	## two hashes which are used in 'readFromLogFile' mode
	my (%psvnsHash,%seedsHash,@seedsArrayFromLogFile);

	if ($method eq 'readFromLogFile') {
		my (@lineArray, $loginID, $seedString);
		$logFileName = "${logsDirectory}${logFileName}";
	   # print "Getting psvn's and problem seeds from $logFileName\n\n";
		$message .= "Getting psvn's and problem seeds from $logFileName\n\n";
		open(BAKLOGFILE,"$logFileName")  or wwerror($0, "Can't open $logFileName");
		while (<BAKLOGFILE>)
			{
			@lineArray = split;
			$loginID= shift(@lineArray);
			$psvnsHash{$loginID} = shift(@lineArray);
			$seedString = join ':', @lineArray;
			$seedsHash{$loginID} = $seedString;
		}
	close(BAKLOGFILE);
	}


	## get a hash of all loginID's for set if set is already defined

	my %loginHashForSet =();
	if (&probSetExists($setNumber)) {%loginHashForSet = %{getLoginHashForSet($setNumber)};}
	my $new_student_count = 0;
	my $existing_student_count = 0;

	my $login_name;

	$message .= "Loading classlist database: ${databaseDirectory}$Global::CL_Database\n\n";
	my @classList = @{getAllLoginNamesSortedByName()};

	foreach $login_name (@classList)   {                  ## read through classlist database and create
												   ## problems for all active students
												   ## except if problems already exist for
		attachCLRecord($login_name);
											   ## student
		my $lastName		= CL_getStudentLastName($login_name);
		my $firstName		= CL_getStudentFirstName($login_name);
		my $status			= CL_getStudentStatus($login_name);
		my $comment			= CL_getComment($login_name);
		my $section			= CL_getClassSection($login_name);
		my $recitation		= CL_getClassRecitation($login_name);
		my $email_address	= CL_getStudentEmailAddress($login_name);
		my $studentID		= CL_getStudentID($login_name);



		unless (&dropStatus($status))                   ## skip students who have dropped the course
			{
			if (defined $loginHashForSet{$login_name}) {
				$existing_student_count++;
				if (((defined $outputFormat) and ($outputFormat eq 'all_students')) or ($method eq 'readFromLogFile')){
					$message .= "   Problems for $firstName $lastName (login $login_name) already exist.  \n";
					$message .= "      Retaining probsetkey $loginHashForSet{$login_name} and data.  \n";
				}
			}
			elsif (($method eq 'readFromLogFile') and !(defined($psvnsHash{$login_name})))
				{
		  #      print "$firstName $lastName (login $login_name) is not in the logfile $logFileName.  \n";
				$message .= "   $firstName $lastName (login $login_name) is not in the logfile $logFileName.  \n";
		  #      print "      Problems for $firstName $lastName (login $login_name) not added.\n";
				$message .= "      Problems for $firstName $lastName (login $login_name) not added.\n";
				}
			elsif (($method eq 'readFromLogFile') and (&attachProbSetRecord($psvnsHash{$login_name}) ))
				{
				$PIN = $psvnsHash{$login_name};
				my $oldLogin = &getStudentLogin($PIN);
				attachCLRecord($oldLogin);

				my $oldFN = &CL_getStudentFirstName($oldLogin);
				my $oldLN =  &CL_getStudentLastName($oldLogin);

#                print "ERROR, ERROR, ERROR\n";
#                print "         The psvn $PIN already exists. It is assigned to $oldFN $oldLN  ($oldLogin).\n";
#                print "         A new problem set must have been created after the psvn $PIN for \n";
#                print "         $firstName $lastName (login $login_name) was deleted.\n";
#				 print "         Problems for $firstName $lastName (login $login_name) have not been added.\n";
#                print "         After restoring all other psvn's for this set from the psvnlog(s),\n";
#                print "         run buildProbSetDB.pl.  This will create a new psvn for  $login_name.\n";
#                print "         Then use the dataMonger to restore the original problem seeds for\n";
#                print "         $login_name.  The seeds for  $login_name  are listed in order in the file\n";
#                print "         $logFileName, on the line begining: $login_name $PIN .\n";
				$message .=
				"ERROR, ERROR, ERROR\n
		 The psvn $PIN already exists. It is assigned to $oldFN $oldLN  ($oldLogin).\n
		 A new problem set must have been created after the psvn $PIN for \n
		 $firstName $lastName (login $login_name) was deleted.\n
		 Problems for $firstName $lastName (login $login_name) have not been added.\n
		 After restoring all other psvn's for this set from the psvnlog(s), use \n
		 Build problem sets from the Professor's page to build this set again.  This \n
		 will create a new psvn for  $login_name.\n
		 Then use Examine or modify data from the Professor's page to restore the original \n
		 problem seeds for $login_name.  The seeds for  $login_name  are listed in order in \n
		 the file $logFileName, on the line begining: $login_name $PIN .\n";

				}
			else
				{
				if ($method eq 'readFromLogFile')
					{
					$PIN = $psvnsHash{$login_name};
					}
				else
					{
					# Create a new unique pin number;
					my $min_psnv = 10**($Global::psvn_digits - 1);
					my $big_psvn = 10**$Global::psvn_digits - $min_psnv - 1;
					$PIN = int (  rand($big_psvn)+$min_psnv ); ##eg rand(8999)+1000
					$i=0;
					# Try for up to 200 times to create a pin number which hasn't been used.
					while ( &attachProbSetRecord($PIN)   ) {
					   # print "psvn Number $PIN already taken\n" if $i>0 ;
						$message .= "psvn Number $PIN already taken\n" if $i>0 ;
						$PIN = int (  rand($big_psvn)+$min_psnv );
						++$i;
						if ($i>200) { wwerror($0, 'Tried 200 times and could not find an unused psvn number.
						  You have run out of psvn numbers.  In Global.pm, increase the variable \$psvn_digits')};
						}#end while loop
					} ##end of if ($method eq 'readFromLogFile')
				#       Store the record of pin numbers in @pinNumbersArray
				#       This can be used to delete pin numbers in case of a failure.

				#       push(@pinNumbersArray, $PIN);

				#       Create a random security number
				#               $securityNumber=int rand(99999);
				#               print "security number is $securityNumber\n";

				#       Install the necessary variables into pinRecord

	#			&putStudentLastName     ($lastName, $PIN);
	#			&putStudentFirstName    ($firstName, $PIN);
	#			&putStudentID           ($studentID, $PIN);
				&putStudentLogin        ($login_name,$PIN);
	#			&putStudentStatus       ($status,$PIN);
	#			&putClassSection        ($section,$PIN);
	#			&putClassRecitation     ($recitation,$PIN);
				&putSetNumber           ($setNumber, $PIN);
				&putSetHeaderFileName   ($setHeaderFileName, $PIN);
				&putProbHeaderFileName   ($probHeaderFileName, $PIN);
				&putDueDate             ($dueDate, $PIN);
				&putOpenDate            ($openDate, $PIN);
				&putAnswerDate          ($answerDate, $PIN);
	#			&putStudentEmailAddress ($email_address, $PIN);

				my @seedList =();
				my ($probNumber,$probSeed);
				#       Generate the problems
				if ($method eq 'readFromLogFile')   {@seedsArrayFromLogFile = split /:/,$seedsHash{$login_name};}
				for($i=0; $i<@problemList; ++$i) {
					$probNumber=$i+1;
					&putProblemFileName     ($problemList[$i],$probNumber, $PIN);

					if ($method eq 'readFromLogFile')   {$probSeed = shift @seedsArrayFromLogFile;}
					else {$probSeed = int( rand(5000) );}

					push (@seedList,$probSeed);  ## put seed in array to be saved later in log file
					&putProblemSeed         ($probSeed,$probNumber, $PIN);
					&putProblemValue        ($problemValueList[$i],$probNumber, $PIN);
					&putProblemMaxNumOfIncorrectAttemps       ($problemAttemptLimitList[$i],$probNumber, $PIN);
					&putProblemAttempted(0,$probNumber, $PIN);
					&putProblemStatus(0,$probNumber, $PIN);
					&putProblemNumOfCorrectAns       (0,$probNumber, $PIN);
					&putProblemNumOfIncorrectAns     (0,$probNumber, $PIN);
				}


				if (&detachProbSetRecord($PIN) )  {
				 # print "probSetKey $PIN for $firstName $lastName (login $login_name) inserted succesfully\n";

				if (
				  ((defined $outputFormat) and ($outputFormat ne 'no_students')) or
				  ($method eq 'readFromLogFile')
				)  {
					$message .= "probSetKey $PIN for $firstName $lastName (login $login_name) inserted succesfully\n";
				}
				  $new_student_count++;
				  #         print "$login_name $PIN @seedList \n";
					print LOGFILE "$login_name $PIN @seedList \n";
				  }
			  else {
				 # print "Couldn't insert probSetKey $PIN\n";
				  $message .= "Couldn't insert probSetKey $PIN\n";
				  }#endif
			  } # end if else
			}#end unless
		}#endforeachLoop
   # print "\n";
	close(LOGFILE);
   # print "DONE\n";
	$message .= "\nData entered for $new_student_count new student(s) in set $setNumber.\n";
	$message .= "Data left unchanged for $existing_student_count existing student(s) in set $setNumber.\n";
	$message .= "\nFINISHED BUILDING PROBLEM SET\n\n";
	$message;
} ### end of sub buildProbSetDB

1;

