#!/usr/bin/perl
## This file is profExportClasslistDatabase.pl
##
####################################################################
# Copyright @ 1995-2000 University of Rochester
# All Rights Reserved
####################################################################
use lib '/ww/webwork/development/'; # mainWeBWorKDirectory;
use CGI qw(:standard);
use Global;
use Auth;
use strict;
use GDBM_File;
my $cgi = new CGI;
my %inputs = $cgi->Vars();
# get information from CGI inputs (see also below for additional information)
my $Course = $inputs{'course'};
my $User = $inputs{'user'};
my $Session_key = $inputs{'key'};
# verify that information has been received
unless($Course && $User && $Session_key) {
&wwerror("$0","The script did not receive the proper input data.","","");
}
# establish environment for this script
&Global::getCourseEnvironment($Course);
my $scriptsDirectory = getWebworkScriptDirectory;
my $databaseDirectory = getCourseDatabaseDirectory;
my $templateDirectory = getCourseTemplateDirectory;
my $cgiURL = getWebworkCgiURL;
my $CL_Database = $Global::CL_Database;
my $path_to_CL_DB = "${databaseDirectory}$CL_Database";
# File names
require "${scriptsDirectory}$Global::HTMLglue_pl";
require "${scriptsDirectory}$Global::DBglue_pl";
require "${scriptsDirectory}$Global::classlist_DBglue_pl";
require "${scriptsDirectory}$Global::FILE_pl";;
my $DELIM = $Global::delim;
# log access
&Global::log_info('', query_string);
my $passwordFile = &Global::getCoursePasswordFile($Course);
my $permissionsFile = &Global::getCoursePermissionsFile($Course);
my $permissions = &get_permissions($inputs{'user'}, $permissionsFile);
my $keyFile = &Global::getCourseKeyFile($Course);
#verify session key
&verify_key($inputs{'user'}, $inputs{'key'}, $keyFile, $Course);
# verify permissions are correct
if ($permissions != $Global::instructor_permissions ) {
print "permissions = $permissions instructor_permissions = $Global::instructor_permissions\n";
print &html_NO_PERMISSION;
exit(0);
}
# get the rest of the information from the submitted form
my $classlistFilename = $inputs{'classList'};
my $update_firstName = $inputs{'update_firstName'};
my $update_lastName = $inputs{'update_lastName'};
my $update_status = $inputs{'update_status'};
my $update_comment = $inputs{'update_comment'};
my $update_section = $inputs{'update_section'};
my $update_recitation = $inputs{'update_recitation'};
my $update_email_address = $inputs{'update_email_address'};
my $update_drop = $inputs{'update_drop'}; ## either 'drop', 'leave', or 'remove'
$update_firstName = 0 unless defined $update_firstName;
$update_lastName = 0 unless defined $update_lastName;
$update_status = 0 unless defined $update_status;
$update_comment = 0 unless defined $update_comment;
$update_section = 0 unless defined $update_section;
$update_recitation = 0 unless defined $update_recitation;
$update_email_address = 0 unless defined $update_email_address;
my $CL_status = get_CL_database_status();
wwerror('Classlist Database is unlocked', 'You must go back and lock the classlist database
before you can export it to an ascii file.') unless $CL_status eq 'locked';
wwerror('No classlist file selected', 'You must go back and select a classlist file.')
unless $classlistFilename =~ /\w/;
my $msg1 = updateClasslistDB($classlistFilename);
my $msg2 = initial_passwords();
my $msg3 = "$msg1" . "$msg2";
uploadSuccess("$msg3");
exit; ## end of main script
sub updateClasslistDB { ## builds the classlist DB and returns a message
my ($classlistFilename) = @_;
#get data from class list.
my $fileName="${templateDirectory}$classlistFilename"; ## e.g. fileName=m161.lst
my $message = "\nGetting classlist file from: $fileName
\n";
checkClasslistFile($Global::noOfFieldsInClasslist,$fileName);
open(FILE, "$fileName") || wwerror($0, "Can't open $fileName");
my @classList=;
close(FILE);
###################################
# Before updating the database we back it up
###################################
if ( -e "$path_to_CL_DB" ) {
$message .= "Backing up current classlist database to: ${path_to_CL_DB}_bak1
\n";
&backup($path_to_CL_DB);
}
my %loginName_StudentID_Hash = %{getLoginName_StudentID_Hash()};
my %studentID_LoginName_Hash =%{getStudentID_LoginName_Hash()};
my $WW_DB_exists = 0;
$WW_DB_exists = 1 if ( -e "${databaseDirectory}$Global::database" );
my %loginName_StudentID_Hash_from_WW_DB =();
my %studentID_LoginName_Hash_from_WW_DB =();
if ($WW_DB_exists) {
%loginName_StudentID_Hash_from_WW_DB =%{getLoginName_StudentID_Hash_from_WW_DB()};
%studentID_LoginName_Hash_from_WW_DB = reverse %loginName_StudentID_Hash_from_WW_DB;
}
my $errors ='';
my %new_good_classlist_students =(); ## students in new classlist without conflicts
my %new_bad_classlist_students =(); ## students in new classlist with conflicts
foreach (@classList) { ## read through classlist and create
## class list database
unless ($_ =~ /\S/) {next;} ## skip blank lines
chomp;
my @classListRecord=&getRecord($_);
my ($studentID, $lastName, $firstName, $status, $comment, $section, $recitation, $email_address, $login_name)
= @classListRecord;
## First we get a list of any conflicts with current students
if ((defined $loginName_StudentID_Hash{$login_name})
and ($loginName_StudentID_Hash{$login_name} ne $studentID)) {
$errors .= "$firstName $lastName, $login_name, $studentID
\n ";
$new_bad_classlist_students{$login_name} =1;
next;
}
if ((defined $studentID_LoginName_Hash{$studentID})
and ($studentID_LoginName_Hash{$studentID} ne $login_name)) {
$errors .= "$firstName $lastName, $login_name, $studentID
\n ";
$new_bad_classlist_students{$login_name} =1;
next;
}
if (($WW_DB_exists) and (defined $loginName_StudentID_Hash_from_WW_DB{$login_name})
and ($loginName_StudentID_Hash_from_WW_DB{$login_name} ne $studentID)) {
$errors .= "$firstName $lastName, $login_name, $studentID
\n ";
$new_bad_classlist_students{$login_name} =1;
next;
}
if (($WW_DB_exists) and (defined $studentID_LoginName_Hash_from_WW_DB{$studentID})
and ($studentID_LoginName_Hash_from_WW_DB{$studentID} ne $login_name)) {
$errors .= "$firstName $lastName, $login_name, $studentID
\n ";
$new_bad_classlist_students{$login_name} =1;
next;
}
## OK, the student record has no conflicts
$new_good_classlist_students{$login_name} =1;
## Handle students already in classlist DB
if (defined $loginName_StudentID_Hash{$login_name}) {
&attachCLRecord($login_name);
&CL_putStudentLastName ($lastName, $login_name) if $update_firstName;
&CL_putStudentFirstName ($firstName, $login_name) if $update_lastName;
&CL_putStudentStatus ($status, $login_name) if $update_status;
&CL_putComment ($comment, $login_name) if $update_comment;
&CL_putClassSection ($section,$login_name) if $update_section;
&CL_putClassRecitation ($recitation,$login_name) if $update_recitation;
&CL_putStudentEmailAddress ($email_address, $login_name) if $update_email_address;
$Global::over_ride_CLBD_lock = 1;
&saveCLRecord($login_name);
$Global::over_ride_CLBD_lock = 0;
if (($WW_DB_exists) and (defined $loginName_StudentID_Hash_from_WW_DB{$login_name})) {
my %setNumberHash=&getAllSetNumbersForStudentLoginHash($login_name);
my @PSVNs = values %setNumberHash;
my $psvn;
foreach $psvn (@PSVNs) {
attachProbSetRecord($psvn);
putStudentLastName( $lastName ,$psvn) if $update_lastName;
putStudentFirstName( $firstName ,$psvn) if $update_firstName;
putStudentStatus( $status ,$psvn) if $update_status;
putClassSection( $section ,$psvn) if $update_section;
putClassRecitation( $recitation ,$psvn) if $update_recitation;
putStudentEmailAddress( $email_address,$psvn) if $update_email_address;
detachProbSetRecord($psvn);
}
}
}
else { ## Handle new students
&CL_putStudentID ($studentID, $login_name);
&CL_putStudentLastName ($lastName, $login_name);
&CL_putStudentFirstName ($firstName, $login_name);
&CL_putStudentStatus ($status, $login_name);
&CL_putComment ($comment, $login_name);
&CL_putClassSection ($section,$login_name);
&CL_putClassRecitation ($recitation,$login_name);
&CL_putStudentEmailAddress ($email_address, $login_name);
$Global::over_ride_CLBD_lock = 1;
&saveCLRecord($login_name);
$Global::over_ride_CLBD_lock = 0;
if (($WW_DB_exists) and (defined $loginName_StudentID_Hash_from_WW_DB{$login_name})) {
my %setNumberHash=&getAllSetNumbersForStudentLoginHash($login_name);
my @PSVNs = values %setNumberHash;
my $psvn;
foreach $psvn (@PSVNs) {
attachProbSetRecord($psvn);
putStudentLastName( $lastName ,$psvn);
putStudentFirstName( $firstName,$psvn);
putStudentStatus( $status ,$psvn);
putClassSection( $section ,$psvn);
putClassRecitation( $recitation ,$psvn);
putStudentEmailAddress( $email_address ,$psvn);
detachProbSetRecord($psvn);
}
}
}
}
## Now we take care of students who are in the current classlist database but are not in
## the classlist file.
my %drop_list =();
my $login_name;
%loginName_StudentID_Hash = %{getLoginName_StudentID_Hash()};
foreach $login_name (keys %loginName_StudentID_Hash) {
$drop_list{$login_name} = 1 unless (
(defined ($new_good_classlist_students{$login_name})) or (defined ($new_bad_classlist_students{$login_name}))
);
}
if ($update_drop eq 'drop') {
my $status = 'D';
$status = $Global::statusDrop[0] if defined $Global::statusDrop[0];
foreach $login_name (keys %drop_list) {
&attachCLRecord($login_name);
&CL_putStudentStatus($status, $login_name);
$Global::over_ride_CLBD_lock = 1;
&saveCLRecord($login_name);
$Global::over_ride_CLBD_lock = 0;
}
}
elsif ($update_drop eq 'remove') {
foreach $login_name (keys %drop_list) {
$Global::over_ride_CLBD_lock = 1;
deleteClassListRecord($login_name);
$Global::over_ride_CLBD_lock = 0;
}
}
else { ## if this case $update_drop eq 'leave' and we do nothing
}
unlock_CL_database();
if ($errors) {
$message .= '
The following students HAVE NOT BEEN ENTERED IN THE CLASSLIST DATABASE
because of a conflict with entries in the WeBWorK problem set database or the classlist database.
These students have a studentID or a loginName that conflicts with a current student.
Enter this information again from the Add Student(s) Page to get a more detailed error message
and instructions on how to correct the problem.
';
$message .= "\n $errors
";
}
$message;
}
sub initial_passwords {
my %studentsinclass=();
my @classListRecord=();
my $msg ='';
# Check that the files exist:
# The permissions file must exist and have both read and write privilages.
# The password file must exist and have both read and write privilages.
unless ( -r $passwordFile and -w $passwordFile) {
wwerror ($0, "Permissions set incorrectly on $passwordFile or its directory.
Cannot access file to both read and write.");
}
unless ( -r $permissionsFile and -w $permissionsFile) {
wwerror ($0, "Permissions set incorrectly on $permissionsFile or its directory.
Cannot access file to both read and write.");
}
my $login_name;
my @classList = @{getAllLoginNames()};
$msg .= "\n
Modifying the password file :\n $passwordFile
\n ";
foreach $login_name (@classList) { ## read through classlist database and create
## passwords for all active students
## except if passwords already exist for student
attachCLRecord($login_name);
my $status = CL_getStudentStatus($login_name);
my $studentID = CL_getStudentID($login_name);
$studentsinclass{$login_name}++ unless(&dropStatus($status));
if(&dropStatus($status)) {
$msg .= ' '."$login_name not added because status is $status
\n ";
}
elsif (&get_password($login_name, $passwordFile)) {
$msg .= ' '."$login_name not added because password already exists
\n ";
}
else {
&new_password($login_name, $studentID, $passwordFile);
&put_permissions(0,$login_name,$permissionsFile);
$msg .= "added: $login_name, $studentID
\n ";
}
}
my @pwStudents = &get_keys_from_db($passwordFile);
my ($ans,$student);
$msg .= "\n
The following login's (if any) in the password and permissions databases are either\n ";
$msg .= "(1) not listed in the new class list database file \n";
$msg .= "or (2) have DROP status in the new class list database file.\n";
$msg .= "They will all be removed from the password and permissions databases.
\n ";
foreach $student (@pwStudents) {
next if defined($studentsinclass{$student});
&delete_password($student,$passwordFile);
&delete_permissions($student,$permissionsFile);
$msg .= "$student
\n ";
}
# ## if the owner of the password file is running this script (e.g. when the password file is first created)
# ## set the permissions correctly
#
# open (PASSWORDFILE, "$passwordFile") or wwerror($0, "Can't open $passwordFile");
# my @stat = stat PASSWORDFILE;
# close PASSWORDFILE;
#
# if ($< == $stat[4]) {
#
# chmod($Global::password_permission, $passwordFile) or
# wwerror($0, "Can't do chmod($Global::password_permission, $passwordFile)");
# chown(-1,$Global::numericalGroupID,$passwordFile) or
# wwerror($0, "Can't do chown(-1,$Global::numericalGroupID,$passwordFile)");
# }
#
# open (PERMISSIONSFILE, "$permissionsFile") or wwerror($0, "Can't open $permissionsFile");
# @stat = stat PERMISSIONSFILE;
# close PERMISSIONSFILE;
#
# if ($< == $stat[4]) {
#
# chmod($Global::permissions_permission, $permissionsFile) or
# wwerror($0, "Can't do chmod($Global::permissions_permission, $permissionsFile)");
# chown(-1,$Global::numericalGroupID,$permissionsFile) or
# wwerror($0, "Can't do chown(-1,$Global::numericalGroupID,$permissionsFile)");
# }
$msg;
}
sub uploadSuccess {
my ($msg) = @_;
print"content-type: text/html\n\nSuccess, the classlist database has been updated.
\n";
print $msg;
print &htmlBOTTOM("profImportClasslistDatabase.pl", \%inputs);
}
sub backup {
## takes as a parameter the full path name
## makes upto two backups of the file with _bak1, or _bak2
## appended to filename where _bak1 is the most recent backup
use File::Copy;
my $fileName =$_[0];
if (-e "${fileName}_bak1") {
rename("${fileName}_bak1","${fileName}_bak2") or
&wwerror("$0","can't rename ${fileName}_bak1");
}
if (-e "${fileName}") {
copy("${fileName}","${fileName}_bak1") or
&wwerror("$0","can't copy ${fileName}");
}
}