From: Mike G. v. a. <we...@ma...> - 2009-01-18 03:34:08
|
Log Message: ----------- changes to support increased error checking when archiving courses Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.77 retrieving revision 1.78 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.77 -r1.78 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -35,7 +35,7 @@ use WeBWorK::Debug; use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive trim_spaces); use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse - listArchivedCourses unarchiveCourse); + listArchivedCourses unarchiveCourse checkCourseTables updateCourseTables); use WeBWorK::Utils::DBImportExport qw(dbExport dbImport); # needed for location management use Net::IP; @@ -185,6 +185,14 @@ } else { $method_to_call = "do_archive_course"; } + } elsif (defined $r->param("upgrade_course_tables") ){ + # upgrade and revalidate + @errors = $self->archive_course_validate; + if (@errors) { + $method_to_call = "archive_course_form"; + } else { + $method_to_call = "archive_course_confirm"; + } } else { # form only $method_to_call = "archive_course_form"; @@ -1639,28 +1647,100 @@ %WeBWorK::SeedCE, courseName => $archive_courseID, }); + if ($r->param("missing_database_tables")) { + my @table_names = split(/\s+/, $r->param("missing_database_tables") ); + warn "tables to be updated @table_names"; + my $msg = updateCourseTables($archive_courseID, $ce2->{dbLayoutName},$ce2,[@table_names]); + print CGI::p({-style=>'color:black; font-weight:bold'}, $msg); + } + my ($tables_ok,$both,$schema_only,$database_only); + my %missing_fields; if ($ce2->{dbLayoutName} ) { + ($tables_ok,$both,$schema_only,$database_only) = checkCourseTables($archive_courseID, $ce2->{dbLayoutName},$ce2); print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID) . "? "); - print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ". - CGI::b($archive_courseID). " after archiving? This cannot be undone!")) if $delete_course_flag; - + print CGI::p({-style=>'color:black; font-weight:bold'},"These schema tables are also found in the database:"); + my $str = ''; + foreach my $table (sort keys %$both) { + my $ok = " ok "; + if ( $both->{$table} =~ /MISSING/) { + $missing_fields{$table} = $both->{$table}; # string containing schema fields and their corresponding database table names + $ok = " missing fields "; + } + + $str .= CGI::b($table).$ok.CGI::br(); + $str .= CGI::span( {-style=>'color:gray; font-weight:lighter'},$both->{$table} ); + } + print CGI::p($str); + # print missing from database + if (%$schema_only) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "These schema tables are missing from the database. + Upgrading the database will create these tables." ); + $str = ''; + foreach my $table (sort keys %$schema_only) { + $str .= CGI::b($table)." missing from database".CGI::br(); + } + print CGI::p($str); + } + # print missing from schema + if (%$database_only) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "These database tables are missing from the schema. + These tables wilthe database before archiving this course." ); + $str = ''; + foreach my $table (sort keys %$database_only) { + $str .= CGI::b($table)." exists in database but is missing from schema".CGI::br(); + } + print CGI::p($str); + } + if ($tables_ok) { + print CGI::p({-style=>'color:black; font-weight:bold'},"Course $archive_courseID database is in order"); + print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ". + CGI::b($archive_courseID). " after archiving? This cannot be undone!")) if $delete_course_flag; + } else { + print CGI::p({-style=>'color:red; font-weight:bold'}, "Course $archive_courseID databases must be updated before archiving this course."); + } + print CGI::start_form(-method=>"POST", -action=>$r->uri); + print $self->hidden_authen_fields; + print $self->hidden_fields("subDisplay"); + print $self->hidden_fields(qw/archive_courseID delete_course/); + # grab some values we'll need + my $course_dir = $ce2->{courseDirs}{root}; + my $archive_path = $ce2->{webworkDirs}{courses} . "/$archive_courseID.tar.gz"; + # fail if the source course does not exist + unless (-e $course_dir) { + print CGI::p( "$archive_courseID: The directory for the course not found."); + } + + # fail if a course archive already exists + # FIXME there could be an option to overwrite an existing archive + if (-e $archive_path and -w $archive_path) { + print CGI::p({-style=>'color:red; font-weight:bold'},"The course '$archive_courseID' has already been archived at '$archive_path'. + This earlier archive will be erased. This cannot be undone."); + } + + + if ($tables_ok and not scalar(%missing_fields) ) { # no missing fields + print CGI::p({style=>"text-align: center"}, + CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"), + " ", + CGI::submit(-name=>"confirm_archive_course", -value=>"archive") , + ); + } else { + print CGI::p({style=>"text-align: center"}, + CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), + CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), + CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), + CGI::submit(-name => "decline_archive_course", -value => "Don't archive"), + " ", + CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), + ); + } + print CGI::end_form(); + } else { + print CGI::p({-style=>'color:red; font-weight:bold'},"Unable to find database layout for $archive_courseID"); } - - print CGI::start_form(-method=>"POST", -action=>$r->uri); - print $self->hidden_authen_fields; - print $self->hidden_fields("subDisplay"); - print $self->hidden_fields(qw/archive_courseID delete_course/); - - print CGI::p({style=>"text-align: center"}, - CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"), - " ", - CGI::submit(-name=>"confirm_archive_course", -value=>"archive"), - ); - - print CGI::end_form(); } sub do_archive_course { |
From: Mike G. v. a. <we...@ma...> - 2009-01-18 03:34:56
|
Log Message: ----------- changes to support increased error checking when archiving courses Modified Files: -------------- webwork2/lib/WeBWorK/Utils: CourseManagement.pm Revision Data ------------- Index: CourseManagement.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils/CourseManagement.pm,v retrieving revision 1.43 retrieving revision 1.44 diff -Llib/WeBWorK/Utils/CourseManagement.pm -Llib/WeBWorK/Utils/CourseManagement.pm -u -r1.43 -r1.44 --- lib/WeBWorK/Utils/CourseManagement.pm +++ lib/WeBWorK/Utils/CourseManagement.pm @@ -32,7 +32,7 @@ use String::ShellQuote; use WeBWorK::CourseEnvironment; use WeBWorK::Debug; -use WeBWorK::Utils qw(runtime_use readDirectory); +use WeBWorK::Utils qw(runtime_use readDirectory pretty_print_rh); our @EXPORT = (); our @EXPORT_OK = qw( @@ -44,6 +44,9 @@ archiveCourse unarchiveCourse dbLayoutSQLSources + checkCourseTables + updateCourseTables + checkCourseDirectories ); =head1 FUNCTIONS @@ -585,7 +588,12 @@ # fail if a course archive already exists # FIXME there could be an option to overwrite an existing archive if (-e $archive_path) { - croak "The course '$courseID' has already been archived at '$archive_path'.\n"; + unlink($archive_path) if (-w $archive_path); + unless (-e $archive_path) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "The archival version of '$courseID' has been replaced'.\n"); + } else { + croak "Unable to replace the archival version of '$courseID'"; + } } #### step 1: dump tables ##### @@ -957,6 +965,14 @@ } } +=over + +=item getHelperRef($helperName, $dbLayoutName) + +Call a database-specific helper function, if a database-layout specific helper +class exists and contains a function named "${helperName}Helper". + +=cut sub getHelperRef { my ($helperName, $dbLayoutName) = @_; @@ -1147,5 +1163,151 @@ print $fh "\n\n\n"; } } +=item checkCourseDirectories($courseName) + +Checks the course files and directories to make sure they exist and have the correct permissions. + +=cut + + + +=item checkCourseTables($courseName, $dbLayoutName, $ce); + +Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout + + +=cut + +sub checkCourseTables { + my ($courseName, $dbLayoutName, $ce) = @_; + my $str=''; + my %both = (); + my %schema_only = (); + my %database_only = (); + ########################################################## + # fetch schema from course environment and search database + # for corresponding tables. + ########################################################## + my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + foreach my $table (sort keys %$db) { + next if $db->{$table}{params}{non_native}; # skip non-native tables + my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; + my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; + if ($database_table_exists ) { # exists means the table could be deleted. + $both{$table_name} = checkTableFields($courseName, $dbLayoutName, $ce, $table); + } else { + $schema_only{$table_name} = 1; + } + } + ########################################################## + # fetch fetch corresponding tables in the database and + # search for corresponding schema entries. + ########################################################## + + my $dbh =$db->{key}->dbh; # grab any database handle + my $stmt = "show tables like '$courseName%'"; # mysql request + my $result = $dbh->selectall_arrayref($stmt) ; + my @tableNames = map {@$_} @$result; # drill down in the result to the table name level + foreach my $table (sort @tableNames) { + $table =~/${courseName}_(.*)/; + my $schema_name = $1; + my $exists = exists($db->{$schema_name}); + $database_only{$table}=1 unless $exists; + } + print CGI::p($str); + my $tables_ok = not ( %schema_only || %database_only ); # count number of extraneous tables; 0 means ok + return ($tables_ok,\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only +} + +=item updateCourseTables($courseName, $dbLayoutName, $ce, $table_names); + +Adds schema tables to the database that had been missing from the database. + +=cut + +sub updateCourseTables { + my ($courseName, $dbLayoutName, $ce, $table_names) = @_; + my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + #die "Programmers: Pass reference to the array of table names to be updated." unless ref($table_names)=~/ARRAY/; + #warn "table names are ".join(" ", @$table_names); + my $str=''; + foreach my $table (sort @$table_names) { # remainder copied from db->create_table + #warn "processing $table"; + next if $table =~ /^_/; # skip non-table self fields (none yet) + #warn "not a non-table self field"; + next if $db->{$table}{params}{non_native}; # skip non-native tables + #warn "not a non_native table"; + my $schema_obj = $db->{$table}; + + if ($schema_obj->can("create_table")) { + # warn "creating table $schema_obj"; + $schema_obj->create_table; + $str .= "Table $table created".CGI::br(); + } else { + warn "Skipping creation of '$table' table: no create_table method\n"; + } + } + $str; + +} + +=cut + + + +=item checkTableFields($courseName, $dbLayoutName, $ce, $table); + +Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout + + +=cut + + +sub checkTableFields { + my ($courseName, $dbLayoutName, $ce,$table) = @_; + my $str=' '; + my %both = (); + my %schema_only = (); + my %database_only = (); + ########################################################## + # fetch schema from course environment and search database + # for corresponding tables. + ########################################################## + my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; + warn "$table_name is a non native table" if $db->{$table}{params}{non_native}; # skip non-native tables + my @fields = $db->{$table}->{record}->FIELDS; + foreach my $field (sort @fields) { + #my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; + my $field_name = $db->{$table}->{params}->{fieldOverride}->{$field} ||$field; + my $database_field_exists = $db->{$table}->tableFieldExists($field_name); + if ($database_field_exists) { + $str.="$field =>$field_name, "; + $both{$field}=1; + } else { + $str.="$field =>MISSING, "; + $schema_only{$field}=1; + } + + } + ########################################################## + # fetch fetch corresponding tables in the database and + # search for corresponding schema entries. + ########################################################## + +# my $dbh =$db->{key}->dbh; # grab any database handle +# my $stmt = "show tables like '$courseName%'"; # mysql request +# my $result = $dbh->selectall_arrayref($stmt) ; +# my @tableNames = map {@$_} @$result; # drill down in the result to the table name level +# foreach my $table (sort @tableNames) { +# $table =~/${courseName}_(.*)/; +# my $schema_name = $1; +# my $exists = exists($db->{$schema_name}); +# $database_only{$table}=1 unless $exists; +# } +# return (\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only + return $str."<br/>"; +} + 1; |