File:  [LON-CAPA] / loncom / debugging_tools / rebuild_db_from_hist.pl
Revision 1.5: download - view: text, annotated - select for diffs
Thu Aug 3 17:27:48 2006 UTC (17 years, 10 months ago) by albertel
Branches: MAIN
CVS tags: HEAD
- added understanding of 'n' hist file commands
- can successfully rebuild even pre 2.2 gradingqueue/slots files

    1: #!/usr/bin/perl -w
    2: #
    3: # The LearningOnline Network
    4: #
    5: # rebuild_db_from_hist.pl Rebuild a *.db file from a *.hist file
    6: #
    7: # $Id: rebuild_db_from_hist.pl,v 1.5 2006/08/03 17:27:48 albertel Exp $
    8: #
    9: # Copyright Michigan State University Board of Trustees
   10: #
   11: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   12: #
   13: # LON-CAPA is free software; you can redistribute it and/or modify
   14: # it under the terms of the GNU General Public License as published by
   15: # the Free Software Foundation; either version 2 of the License, or
   16: # (at your option) any later version.
   17: #
   18: # LON-CAPA is distributed in the hope that it will be useful,
   19: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   20: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   21: # GNU General Public License for more details.
   22: #
   23: # You should have received a copy of the GNU General Public License
   24: # along with LON-CAPA; if not, write to the Free Software
   25: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   26: #
   27: # /home/httpd/html/adm/gpl.txt
   28: #
   29: # http://www.lon-capa.org/
   30: #
   31: #################################################
   32: use strict;
   33: use lib '/home/httpd/lib/perl';
   34: use Getopt::Long;
   35: use GDBM_File;
   36: use LONCAPA;
   37: use Apache::lonnet;
   38: 
   39: #
   40: # Options
   41: my ($help,$debug,$test,$p_is_s);
   42: GetOptions("help"    => \$help,
   43:            "debug"   => \$debug,
   44:            "test"    => \$test,
   45:            "p_is_s"  => \$p_is_s);
   46: 
   47: if (! defined($debug))   { $debug   = 0; }
   48: if (! defined($test))    { $test    = 0; }
   49: 
   50: #
   51: # Help them out if they ask for it
   52: if ($help) {
   53:     print <<'END';
   54: rebuild_db_from_hist.pl - recreate a db file from a hist file.
   55: Options:
   56:    -help     Display this help.
   57:    -debug    Output debugging code (not much is output yet)
   58:    -test     Verify the given *.hist file will reconstruct the current db file
   59:              Sends error messages to STDERR.
   60:    -p_is_s   Treat 'P' lines as 'S' lines.
   61: Examples: 
   62:     rebuild_db_from_hist.pl -t $file.hist  # Perform a test rebuild
   63:     rebuild_db_from_hist.pl $file.hist       
   64: END
   65:     exit;
   66: }
   67: 
   68: #
   69: # Loop through ARGV getting files.
   70: while (my $fname = shift) {
   71:     if ($fname !~ m/\.hist$/) {
   72: 	print("error: $fname is not a hist file");
   73: 	next;
   74:     }
   75: 
   76:     my $db_filename = $fname;
   77:     $db_filename =~ s/\.hist$/\.db/;
   78:     if (-e $db_filename && ! $test) {
   79:         print "Aborting: The target file $db_filename exists.".$/;
   80:         next;
   81:     }
   82:     my ($error,$constructed_hash) = &process_file($fname,$db_filename,$debug);
   83:     if (! defined($error) ) {
   84: 	$error = &update_hash($db_filename,$constructed_hash);
   85:     }
   86:     if (! defined($error) || ! $test) {
   87:         $error = &write_hash($db_filename,$constructed_hash);
   88:     }
   89:     if ($test) {
   90:         $error = &write_hash($db_filename.'.test',$constructed_hash);
   91:     }
   92:     if ($test) {
   93:         my $error = &test_hash($db_filename,$constructed_hash);
   94:         if (defined($error)) {
   95:             print "Error processing ".$fname.$/;
   96:             print $error;
   97:         } else {
   98:             print "Everything looks good for ".$fname.$/;
   99:         }
  100:     }
  101:     if (defined($error)) {
  102:         print $error.$/;
  103:     }
  104: }
  105: 
  106: exit;
  107: 
  108: ######################################################
  109: ######################################################
  110: sub process_file {
  111:     my ($fname,$db_filename,$debug) = @_;
  112:     #
  113:     open(HISTFILE,$fname);
  114:     my %db_to_store;
  115:     my $no_action_count = 0;
  116:     while (my $command = <HISTFILE>) {
  117:         chomp($command);
  118:         my $error = undef;
  119:         # Each line can begin with:
  120:         #  P:put
  121:         #  S:store
  122:         #  D:delete
  123:         #  N:new put (only adds tha values if they are all new values)
  124:         my ($action,$time,$concatenated_data) = split(':',$command,3);
  125:         if ($fname eq 'roles.hist' && $concatenated_data =~ /^.*:.*:/) {
  126:             (undef,undef,$concatenated_data) = split(':',$concatenated_data,3);
  127:         }
  128:         next if (! defined($action));
  129:         if ($action eq 'P' && $p_is_s) { $action = 'S'; }
  130:         my ($rid,$allkeys,$version);
  131:         if ($action eq 'S') {
  132:             ($rid,$concatenated_data) = split(':',$concatenated_data,2);
  133:             $version = ++$db_to_store{"version:$rid"};
  134:              #print $version.$/;
  135:         }
  136:         next if (! defined($concatenated_data));
  137: 	my $add_new_data = 1;
  138:         my @data = split('&',$concatenated_data);
  139:         foreach my $k_v_pair (@data) {
  140:             my ($key,$value) = split('=',$k_v_pair,2);
  141:             if (defined($action) && $action eq 'P') {
  142:                 if (defined($value)) {
  143:                     $db_to_store{$key}=$value;
  144:                 } else {
  145:                     $no_action_count++;
  146:                 }
  147:             } elsif ($action eq 'S') {
  148:                 # Versioning of data, so we update the old ata
  149:                 $allkeys.=$key.':';
  150:                 $db_to_store{"$version:$rid:$key"}=$value;
  151:             } elsif ($action eq 'N') {
  152:                 if (exists($db_to_store{$key})) {
  153: 		    $add_new_data = 0;
  154: 		    print "exists $key\n";
  155: 		}
  156:             } elsif ($action eq 'D') {
  157:                 delete($db_to_store{$key});
  158:             } else {
  159:                 $error = "Unable to understand action '".$action."'";
  160:             }
  161:         }
  162: 	if ($action eq 'N' && $add_new_data) {
  163: 	    foreach my $k_v_pair (@data) {
  164: 		my ($key,$value) = split('=',$k_v_pair,2);
  165: 		$db_to_store{$key}=$value;
  166: 	    }
  167: 	}
  168:         if ($action eq 'S') {
  169: 	    $db_to_store{"$version:$rid:timestamp"}=$time;
  170: 	    $allkeys.='timestamp';
  171: 	    $db_to_store{"$version:keys:$rid"}=$allkeys;
  172:         }
  173:         if (defined($error)) {
  174:             return ('Error:'.$error.$/,undef);
  175:         }
  176:     }
  177:     if ($no_action_count) {
  178:         print $no_action_count.' lines did not require action.'.$/;
  179:     }
  180:     close(HISTFILE);
  181:     return (undef,\%db_to_store);
  182: }
  183: 
  184: sub write_hash {
  185:     my ($db_filename,$db_to_store) = @_;
  186:     #
  187:     # Write the gdbm file
  188:     my %db;
  189:     if (! tie(%db,'GDBM_File',$db_filename,&GDBM_WRCREAT(),0640)) {
  190:         warn "Unable to tie to $db_filename";
  191:         return "Unable to tie to $db_filename";
  192:     }
  193:     #
  194:     while (my ($k,$v) = each(%$db_to_store)) {
  195:         $db{$k}=$v;
  196:     }
  197:     #
  198:     untie(%db);
  199:     return undef;
  200: }
  201: 
  202: sub test_hash {
  203:     my ($db_filename,$my_db) = @_;
  204:     #
  205:     my %db;
  206:     if (! tie(%db,'GDBM_File',$db_filename,&GDBM_READER(),0640)) {
  207:         return "Unable to tie to $db_filename";;
  208:     }
  209:     my (%key_errors,%value_errors);
  210:     while (my ($k,$v) = each(%db)) {
  211:         if (exists($my_db->{$k})) {
  212:             if ($my_db->{$k} eq $v) {
  213:                 delete($my_db->{$k});
  214:             } else {
  215:                 $value_errors{$k}=$v;
  216:             }
  217:         } else {
  218:             $key_errors{$k}=$v;
  219:         }
  220:     }
  221:     untie(%db);
  222:     #
  223:     my $error;
  224:     my $extra_count = scalar(keys(%$my_db));
  225:     if ($extra_count) {
  226:         $error.=$extra_count.' extra key/value pairs found in hist: '.$/;
  227:         while (my ($k,$v) = each(%$my_db)) {
  228: 	    $error .= '  "'.$k.'" => "'.$v.'"'.$/;
  229:         }
  230:     }
  231:     my $key_count = scalar(keys(%key_errors));
  232:     if ($key_count) {
  233:         $error.=$key_count.' missing keys found in db but not in hist: '.$/;
  234:         while (my ($k,$v) = each(%key_errors)) {
  235:             $error .= '  "'.$k.'" => "'.$v.'"'.$/;
  236:         }
  237:     }
  238:     my $value_count = scalar(keys(%value_errors));
  239:     if ($value_count) {
  240:         $error.=$value_count.' mismatched values found: '.$/;
  241:         while (my ($k,$v) = each(%value_errors)) {
  242:             $error .= '  "'.$k.'" => "'.$v.'"'.$/;
  243:         }
  244:     }
  245:     #
  246:     return $error;
  247: }
  248: 
  249: sub update_hash {
  250:     my ($db_filename,$my_db) = @_;
  251:     if ($db_filename=~
  252: 	m/(gradingqueue|reviewqueue|slots|slot_reservations).db$/) {
  253: 	&update_grading_queue($db_filename,$my_db);
  254:     }
  255: }
  256: 
  257: sub update_grading_queue {
  258:     my ($db_filename,$my_db) = @_;
  259:     my ($name) = 
  260: 	($db_filename=~m/(gradingqueue|reviewqueue|slots|slot_reservations).db$/);
  261:     my $type='queue';
  262:     if ($name eq 'slots') {
  263: 	$type = 'slots';
  264:     } elsif ($name eq 'slot_reservations') {
  265: 	$type = 'reservation';
  266:     }
  267:     if ($type eq 'queue') {
  268: 	foreach my $key (keys(%{$my_db})) {
  269: 	    my $real_key = &unescape($key);
  270: 	    my (@elements) = split("\0",$real_key);
  271: 	    if (exists($elements[2])) {
  272: 		$elements[2] = &update_value($elements[2]);
  273: 	    }
  274: 	    $real_key = join("\0",@elements);
  275: 	    my $new_key = &escape($real_key);
  276: 	    if ($new_key ne $key) {
  277: 		$my_db->{$new_key} = $my_db->{$key};
  278: 		delete($my_db->{$key});
  279: 	    }
  280: 	    if ($new_key =~ /locked$/) {
  281: 		my $value = $my_db->{$new_key};
  282: 		my $new_value = &unescape($value);
  283: 		$new_value = &update_value($new_value);
  284: 		$my_db->{$new_key} = &escape($new_value);
  285: 	    }
  286: 	}
  287:     } elsif ($type eq 'slots') {
  288: 	foreach my $key (keys(%{$my_db})) {
  289: 	    my $value = $my_db->{$key};
  290: 	    $value = &Apache::lonnet::thaw_unescape($value);
  291: 	    if (exists($value->{'proctor'})) {
  292: 		$value->{'proctor'} = &update_value($value->{'proctor'});
  293: 	    }
  294: 	    if (exists($value->{'allowedusers'})) {
  295: 		$value->{'allowedusers'} = 
  296: 		    &update_value($value->{'allowedusers'});
  297: 	    }
  298: 	    $my_db->{$key} = &Apache::lonnet::freeze_escape($value);
  299: 	}
  300:     } elsif ($type eq 'reservation') {
  301: 	foreach my $key (keys(%{$my_db})) {
  302: 	    my $value = $my_db->{$key};
  303: 	    $value = &Apache::lonnet::thaw_unescape($value);
  304: 	    if (exists($value->{'name'})) {
  305: 		$value->{'name'} = &update_value($value->{'name'});
  306: 	    }
  307: 	    $my_db->{$key} = &Apache::lonnet::freeze_escape($value);
  308: 	}
  309:     }
  310: }
  311: 
  312: sub update_value {
  313:     my ($value) = @_;
  314:     if ($value =~ /@/ && $value !~ /:/) {
  315: 	$value =~ tr/@/:/;
  316:     }
  317:     return $value;
  318: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>