From: Mike G. v. a. <we...@ma...> - 2005-08-14 16:50:39
|
Log Message: ----------- Add the beginnings of a facility for archiving a course. This method uses mysqldump and therefore will work only with a mysql database. Many things are still hardwired In particular mysqldump is hardwired. The database name is hardwired to "webwork" Archiving consists of dumping the tables associated to the course to a file $courseID/DATA/$courseID_mysql.database (this file can be used to created a new database for the course. it will not overwrite existing tables however.) Then the entire course directory is tarred and gzipped and placed in the courses directory with the naem $courseID.tar.gz Currently nothing is deleted from the database and no directories are deleted. So the implementation is fairly safe, but not yet very useful for course management. There is not yet a facility for automatically importing the archived course. Modified Files: -------------- webwork-modperl/lib/WeBWorK/Utils: CourseManagement.pm webwork-modperl/lib/WeBWorK/Utils/CourseManagement: sql_single.pm Revision Data ------------- Index: CourseManagement.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/Utils/CourseManagement.pm,v retrieving revision 1.23 retrieving revision 1.24 diff -Llib/WeBWorK/Utils/CourseManagement.pm -Llib/WeBWorK/Utils/CourseManagement.pm -u -r1.23 -r1.24 --- lib/WeBWorK/Utils/CourseManagement.pm +++ lib/WeBWorK/Utils/CourseManagement.pm @@ -38,6 +38,7 @@ addCourse renameCourse deleteCourse + archiveCourse dbLayoutSQLSources ); @@ -454,6 +455,125 @@ } } + +=item archiveCourse(%options) + +%options must contain: + + courseID => $courseID, + ce => $ce, + dbOptions => $dbOptions, + newCourseID => $newCourseID, + +Archive the course named $courseID in the $webworkDirs{courses} directory +as $webworkDirs{courses}/$courseID.tar.gz. The data from the database is +stored in several files at $courseID/DATA/$table_name.txt before the course's directories +are tarred and gzipped. The table names are $courseID_user, $courseID_set +and so forth. Only files and directories stored directly in the course directory +are archived. The contents of linked files is not archived although the symbolic links +themselves are saved. + +$ce is a WeBWorK::CourseEnvironment object that describes the existing course's +environment. + +# $dbOptions is a reference to a hash containing information required to create +# the course's new database and delete the course's old database. +# +# if dbLayout == "sql": +# +# host => host to connect to +# port => port to connect to +# username => user to connect as (must have CREATE, DELETE, FILE, INSERT, +# SELECT, UPDATE privileges, WITH GRANT OPTION.) +# password => password to supply +# old_database => the name of the database to delete +# new_database => the name of the database to create +# wwhost => the host from which the webwork database users will be allowed +# to connect. (if host is set to localhost, this should be set to +# localhost too.) +# +# The name of the course's directory is changed to $newCourseID. + +If the course's database layout is C<sql_single>, the contents of +the courses database tables are exported to text files using the sql database's +export facility. Then the tables are deleted from the database. + +# If the course's database layout is C<sql>, a new database is created, course +# data is copied from the old database to the new database, and the old database +# is deleted. +# +# If the course's database layout is C<gdbm>, the DBM files are simply renamed on +# disk. + +If the course's database layout is something else, no database changes are made. + +Any errors encountered while renaming the course are returned. + +=cut + +sub archiveCourse { + my (%options) = @_; + + # renameCourseHelper needs: + # $fromCourseID ($oldCourseID) + # $fromCE ($ce) + # $toCourseID ($newCourseID) + # $toCE (construct from $ce) + # $dbLayoutName ($ce->{dbLayoutName}) + # %options ($dbOptions) + + my $courseID = $options{courseID}; + my $ce = $options{ce}; + my %dbOptions = defined $options{dbOptions} ? %{ $options{dbOptions} } : (); + + + # get the database layout out of the options hash + my $dbLayoutName = $ce->{dbLayoutName}; + + die "I happen to know that renameCourse() will only succeed for sql_single courses. Bug Mike to write support for gdbm and sql courses.\n" + unless $dbLayoutName eq "sql_single"; + + # collect some data + my $coursesDir = $ce->{webworkDirs}->{courses}; + my $courseDir = "$coursesDir/$courseID"; + my $dataDir = "$courseDir/DATA"; + my $archivePath = "$coursesDir/$courseID.tar.gz"; + + # create DATA directory if it does not exist. + unless (-e $dataDir) { + mkdir "$dataDir" or die "Failed to create course directory $dataDir"; + } + # fail if the target file already exists + if (-e $archivePath) { + croak "The course $courseID has already been archived at $archivePath"; + } + + # fail if the source course does not exist + unless (-e $courseDir) { + croak "$courseID: course not found"; + } + + $dbOptions{archiveDatabasePath} = "$dataDir/${courseID}_mysql.database"; + ##### step 1: export database contents ###### + # munge DB options to move new_database => database + + + my $archiveHelperResult = archiveCourseHelper($courseID, $ce, $dbLayoutName, %dbOptions); + die "$courseID: course database dump failed.\n" unless $archiveHelperResult; + + ##### step 2: tar and gzip course directory ##### + + # archive top-level course directory + #FIXME (check) don't follow links + my $tarCmd = $ce->{externalPrograms}->{tar}; + debug("archiving course dir: $tarCmd $archivePath $courseDir \n"); + my $tarStatement = "$tarCmd -zcf $archivePath $courseDir"; + my $tarResult = system $tarStatement ; + $tarResult and die "failed to tar course directory with command: '$tarStatement ' (errno: $tarResult): $!\n"; + +} + + =item dbLayoutSQLSources($dbLayout) Retrun a hash of database sources for the sql and sql_single database layouts. @@ -543,6 +663,18 @@ return $result; } +=item archiveCourseHelper($courseID, $ce, $dbLayoutName, %options) + +Perform database-layout specific operations for archiving the data in a course. + +=cut + +sub archiveCourseHelper { + my ($courseID, $ce, $dbLayoutName, %options) = @_; + my $result = callHelperIfExists("archiveCourseHelper", $dbLayoutName, @_); + return $result; +} + =item copyCourseDataHelper($fromCourseID, $fromCE, $toCourseID, $toCE, $dbLayoutName, %options) Perform database-layout specific operations for copying a course's data from one Index: sql_single.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/Utils/CourseManagement/sql_single.pm,v retrieving revision 1.5 retrieving revision 1.6 diff -Llib/WeBWorK/Utils/CourseManagement/sql_single.pm -Llib/WeBWorK/Utils/CourseManagement/sql_single.pm -u -r1.5 -r1.6 --- lib/WeBWorK/Utils/CourseManagement/sql_single.pm +++ lib/WeBWorK/Utils/CourseManagement/sql_single.pm @@ -171,7 +171,7 @@ return 1; } -=item renameCourseHelper($fromCourseID, $fromCE, $toCourseID, $toCE, $dbLayoutName, %options) +=item copyCourseHelper($fromCourseID, $fromCE, $toCourseID, $toCE, $dbLayoutName, %options) Uses addCourseHelper() to create a new course database on the same server. Copies the data from the old course database to the new one. Uses @@ -290,6 +290,89 @@ return 1; } +=item archiveCourseHelper($fromCourseID, $fromCE, $toCourseID, $toCE, $dbLayoutName, %options) + +Dumps the data from the course database to text files in the courseID/DATA directory. Uses +deleteCourseHelper() to delete the old course database. + +=cut + +sub archiveCourseHelper { + my ($courseID, $ce, $dbLayoutName, %options) = @_; + debug("courseID=$courseID, ce=$ce dbLayoutName=$dbLayoutName\n"); + + ##### get list of tables to archive ##### + + my $dbLayout = $ce->{dbLayouts}->{$dbLayoutName}; + debug("dbLayout=$dbLayout\n"); + my %sources = dbLayoutSQLSources($dbLayout); + debug("fSources: ", Dumper(\%sources)); + my $source = mostPopularSource(%sources); + debug("source=$source\n"); + my %source = %{ $sources{$source} }; + my @tables = @{ $source{tables} }; + my $username = $source{username}; + my $password = $source{password}; + my $archiveDatabasePath = $options{archiveDatabasePath}; + + ##### construct SQL statements to copy the data in each table ##### + + my @stmts; + my @dataTables = (); + foreach my $table (@tables) { + debug("Table: $table\n"); + my $table = do { + my $paramsRef = $dbLayout->{$table}->{params}; + if ($paramsRef) { + if (exists $paramsRef->{tableOverride}) { + $paramsRef->{tableOverride} + } else { + ""; # no override + } + } else { + ""; # no params + } + } || $table; + debug("sql \"real\" table name: $table\n"); + + + # this method would be mysql specific but it's a start + # mysqldump --user=$username --password=$password database tables +# my $stmt = "DUMP SELECT * FROM `$fromTable`"; +# debug("stmt = $stmt\n"); +# push @stmts, $stmt; + push @dataTables, $table; + } + debug("Database tables to export are ",join(" ", @dataTables)); + # this method would be mysql specific but it's a start + my $exportStatement = " mysqldump --user=$username ". + "--password=$password " . + " webwork ". + join(" ", @dataTables). + " >$archiveDatabasePath"; + debug($exportStatement); + my $exportResult = system $exportStatement; + $exportResult and die "failed to tar course directory with command: '$exportResult ' (errno: $exportResult): $!\n"; + + ##### issue SQL statements ##### + +# my $dbh = DBI->connect($source, $username, $password); +# unless (defined $dbh) { +# die "sql_single: failed to connect to DBI source '$source': $DBI::errstr\n"; +# } +# +# foreach my $stmt (@stmts) { +# my $rows = $dbh->do($stmt); +# unless (defined $rows) { +# die "sql_single: failed to execute SQL statement '$stmt': $DBI::errstr\n"; +# } +# } +# +# $dbh->disconnect; + + return 1; +} + # returns the name of the source with the most tables sub mostPopularSource { my (%sources) = @_; |