Annotation of loncom/debugging_tools/rebuild_db_from_hist.pl, revision 1.6

1.1       matthew     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: #
1.6     ! albertel    7: # $Id: rebuild_db_from_hist.pl,v 1.5 2006/08/03 17:27:48 albertel Exp $
1.1       matthew     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;
1.5       albertel   33: use lib '/home/httpd/lib/perl';
1.1       matthew    34: use Getopt::Long;
                     35: use GDBM_File;
1.5       albertel   36: use LONCAPA;
                     37: use Apache::lonnet;
1.1       matthew    38: 
                     39: #
                     40: # Options
1.6     ! albertel   41: my ($help,$debug,$test,$test_db,$p_is_s);
        !            42: GetOptions("help"           => \$help,
        !            43:            "debug"          => \$debug,
        !            44:            "test"           => \$test,
        !            45:            "create_test_db" => \$test_db,
        !            46:            "p_is_s"         => \$p_is_s);
1.1       matthew    47: 
                     48: if (! defined($debug))   { $debug   = 0; }
                     49: if (! defined($test))    { $test    = 0; }
                     50: 
                     51: #
                     52: # Help them out if they ask for it
                     53: if ($help) {
                     54:     print <<'END';
                     55: rebuild_db_from_hist.pl - recreate a db file from a hist file.
                     56: Options:
                     57:    -help     Display this help.
1.2       matthew    58:    -debug    Output debugging code (not much is output yet)
                     59:    -test     Verify the given *.hist file will reconstruct the current db file
                     60:              Sends error messages to STDERR.
1.6     ! albertel   61:    -create_test_db
        !            62:              when testing also create a *.db.test db of the testing info
1.4       matthew    63:    -p_is_s   Treat 'P' lines as 'S' lines.
1.1       matthew    64: Examples: 
1.2       matthew    65:     rebuild_db_from_hist.pl -t $file.hist  # Perform a test rebuild
                     66:     rebuild_db_from_hist.pl $file.hist       
1.1       matthew    67: END
                     68:     exit;
                     69: }
                     70: 
                     71: #
                     72: # Loop through ARGV getting files.
                     73: while (my $fname = shift) {
1.5       albertel   74:     if ($fname !~ m/\.hist$/) {
                     75: 	print("error: $fname is not a hist file");
                     76: 	next;
                     77:     }
                     78: 
1.1       matthew    79:     my $db_filename = $fname;
                     80:     $db_filename =~ s/\.hist$/\.db/;
                     81:     if (-e $db_filename && ! $test) {
1.6     ! albertel   82:         print STDERR "Aborting: The target file $db_filename exists.".$/;
1.1       matthew    83:         next;
                     84:     }
1.3       matthew    85:     my ($error,$constructed_hash) = &process_file($fname,$db_filename,$debug);
1.5       albertel   86:     if (! defined($error) ) {
                     87: 	$error = &update_hash($db_filename,$constructed_hash);
                     88:     }
1.1       matthew    89:     if (! defined($error) || ! $test) {
                     90:         $error = &write_hash($db_filename,$constructed_hash);
                     91:     }
1.6     ! albertel   92:     if ($test && $test_db) {
1.5       albertel   93:         $error = &write_hash($db_filename.'.test',$constructed_hash);
                     94:     }
                     95:     if ($test) {
1.1       matthew    96:         my $error = &test_hash($db_filename,$constructed_hash);
                     97:         if (defined($error)) {
                     98:             print "Error processing ".$fname.$/;
1.6     ! albertel   99:             print STDERR $error;
1.1       matthew   100:         } else {
                    101:             print "Everything looks good for ".$fname.$/;
                    102:         }
                    103:     }
                    104:     if (defined($error)) {
                    105:         print $error.$/;
                    106:     }
                    107: }
                    108: 
                    109: exit;
                    110: 
                    111: ######################################################
                    112: ######################################################
                    113: sub process_file {
                    114:     my ($fname,$db_filename,$debug) = @_;
                    115:     #
                    116:     open(HISTFILE,$fname);
                    117:     my %db_to_store;
                    118:     my $no_action_count = 0;
                    119:     while (my $command = <HISTFILE>) {
                    120:         chomp($command);
                    121:         my $error = undef;
                    122:         # Each line can begin with:
                    123:         #  P:put
1.5       albertel  124:         #  S:store
1.1       matthew   125:         #  D:delete
1.5       albertel  126:         #  N:new put (only adds tha values if they are all new values)
1.6     ! albertel  127: 	#  M:modify the values for a previous S
1.1       matthew   128:         my ($action,$time,$concatenated_data) = split(':',$command,3);
1.3       matthew   129:         if ($fname eq 'roles.hist' && $concatenated_data =~ /^.*:.*:/) {
                    130:             (undef,undef,$concatenated_data) = split(':',$concatenated_data,3);
                    131:         }
1.4       matthew   132:         next if (! defined($action));
                    133:         if ($action eq 'P' && $p_is_s) { $action = 'S'; }
1.6     ! albertel  134:         my ($rid,@allkeys,$version);
1.4       matthew   135:         if ($action eq 'S') {
                    136:             ($rid,$concatenated_data) = split(':',$concatenated_data,2);
                    137:             $version = ++$db_to_store{"version:$rid"};
1.5       albertel  138:              #print $version.$/;
1.4       matthew   139:         }
1.6     ! albertel  140: 	if ($action eq 'M') {
        !           141:             ($rid,$version,$concatenated_data) = 
        !           142: 		split(':',$concatenated_data,3);
        !           143: 	}
1.4       matthew   144:         next if (! defined($concatenated_data));
1.5       albertel  145: 	my $add_new_data = 1;
1.1       matthew   146:         my @data = split('&',$concatenated_data);
                    147:         foreach my $k_v_pair (@data) {
                    148:             my ($key,$value) = split('=',$k_v_pair,2);
                    149:             if (defined($action) && $action eq 'P') {
                    150:                 if (defined($value)) {
                    151:                     $db_to_store{$key}=$value;
                    152:                 } else {
                    153:                     $no_action_count++;
                    154:                 }
1.6     ! albertel  155:             } elsif ($action eq 'S' || $action eq 'M') {
1.4       matthew   156:                 # Versioning of data, so we update the old ata
1.6     ! albertel  157:                 push(@allkeys,$key);
1.4       matthew   158:                 $db_to_store{"$version:$rid:$key"}=$value;
1.5       albertel  159:             } elsif ($action eq 'N') {
                    160:                 if (exists($db_to_store{$key})) {
                    161: 		    $add_new_data = 0;
                    162: 		    print "exists $key\n";
                    163: 		}
1.1       matthew   164:             } elsif ($action eq 'D') {
                    165:                 delete($db_to_store{$key});
                    166:             } else {
                    167:                 $error = "Unable to understand action '".$action."'";
                    168:             }
                    169:         }
1.6     ! albertel  170: 
1.5       albertel  171: 	if ($action eq 'N' && $add_new_data) {
                    172: 	    foreach my $k_v_pair (@data) {
                    173: 		my ($key,$value) = split('=',$k_v_pair,2);
                    174: 		$db_to_store{$key}=$value;
                    175: 	    }
                    176: 	}
1.6     ! albertel  177: 	if ($action eq 'S') {
1.4       matthew   178: 	    $db_to_store{"$version:$rid:timestamp"}=$time;
1.6     ! albertel  179: 	    push(@allkeys,'timestamp');
        !           180: 	}
        !           181:         if ($action eq 'S' || $action eq 'M') {
        !           182: 	    $db_to_store{"$version:keys:$rid"}=join(':',@allkeys);
1.4       matthew   183:         }
1.1       matthew   184:         if (defined($error)) {
                    185:             return ('Error:'.$error.$/,undef);
                    186:         }
                    187:     }
                    188:     if ($no_action_count) {
                    189:         print $no_action_count.' lines did not require action.'.$/;
                    190:     }
                    191:     close(HISTFILE);
                    192:     return (undef,\%db_to_store);
                    193: }
                    194: 
                    195: sub write_hash {
                    196:     my ($db_filename,$db_to_store) = @_;
                    197:     #
                    198:     # Write the gdbm file
                    199:     my %db;
                    200:     if (! tie(%db,'GDBM_File',$db_filename,&GDBM_WRCREAT(),0640)) {
                    201:         warn "Unable to tie to $db_filename";
                    202:         return "Unable to tie to $db_filename";
                    203:     }
                    204:     #
                    205:     while (my ($k,$v) = each(%$db_to_store)) {
                    206:         $db{$k}=$v;
                    207:     }
                    208:     #
                    209:     untie(%db);
                    210:     return undef;
                    211: }
                    212: 
                    213: sub test_hash {
                    214:     my ($db_filename,$my_db) = @_;
                    215:     #
                    216:     my %db;
                    217:     if (! tie(%db,'GDBM_File',$db_filename,&GDBM_READER(),0640)) {
                    218:         return "Unable to tie to $db_filename";;
                    219:     }
                    220:     my (%key_errors,%value_errors);
                    221:     while (my ($k,$v) = each(%db)) {
                    222:         if (exists($my_db->{$k})) {
                    223:             if ($my_db->{$k} eq $v) {
                    224:                 delete($my_db->{$k});
                    225:             } else {
                    226:                 $value_errors{$k}=$v;
                    227:             }
                    228:         } else {
                    229:             $key_errors{$k}=$v;
                    230:         }
                    231:     }
                    232:     untie(%db);
                    233:     #
                    234:     my $error;
                    235:     my $extra_count = scalar(keys(%$my_db));
                    236:     if ($extra_count) {
1.4       matthew   237:         $error.=$extra_count.' extra key/value pairs found in hist: '.$/;
1.1       matthew   238:         while (my ($k,$v) = each(%$my_db)) {
1.5       albertel  239: 	    $error .= '  "'.$k.'" => "'.$v.'"'.$/;
1.1       matthew   240:         }
                    241:     }
                    242:     my $key_count = scalar(keys(%key_errors));
                    243:     if ($key_count) {
1.4       matthew   244:         $error.=$key_count.' missing keys found in db but not in hist: '.$/;
1.1       matthew   245:         while (my ($k,$v) = each(%key_errors)) {
                    246:             $error .= '  "'.$k.'" => "'.$v.'"'.$/;
                    247:         }
                    248:     }
                    249:     my $value_count = scalar(keys(%value_errors));
                    250:     if ($value_count) {
1.4       matthew   251:         $error.=$value_count.' mismatched values found: '.$/;
1.1       matthew   252:         while (my ($k,$v) = each(%value_errors)) {
                    253:             $error .= '  "'.$k.'" => "'.$v.'"'.$/;
                    254:         }
                    255:     }
                    256:     #
                    257:     return $error;
                    258: }
1.5       albertel  259: 
                    260: sub update_hash {
                    261:     my ($db_filename,$my_db) = @_;
                    262:     if ($db_filename=~
                    263: 	m/(gradingqueue|reviewqueue|slots|slot_reservations).db$/) {
                    264: 	&update_grading_queue($db_filename,$my_db);
                    265:     }
                    266: }
                    267: 
                    268: sub update_grading_queue {
                    269:     my ($db_filename,$my_db) = @_;
                    270:     my ($name) = 
                    271: 	($db_filename=~m/(gradingqueue|reviewqueue|slots|slot_reservations).db$/);
                    272:     my $type='queue';
                    273:     if ($name eq 'slots') {
                    274: 	$type = 'slots';
                    275:     } elsif ($name eq 'slot_reservations') {
                    276: 	$type = 'reservation';
                    277:     }
                    278:     if ($type eq 'queue') {
                    279: 	foreach my $key (keys(%{$my_db})) {
                    280: 	    my $real_key = &unescape($key);
                    281: 	    my (@elements) = split("\0",$real_key);
                    282: 	    if (exists($elements[2])) {
                    283: 		$elements[2] = &update_value($elements[2]);
                    284: 	    }
                    285: 	    $real_key = join("\0",@elements);
                    286: 	    my $new_key = &escape($real_key);
                    287: 	    if ($new_key ne $key) {
                    288: 		$my_db->{$new_key} = $my_db->{$key};
                    289: 		delete($my_db->{$key});
                    290: 	    }
                    291: 	    if ($new_key =~ /locked$/) {
                    292: 		my $value = $my_db->{$new_key};
                    293: 		my $new_value = &unescape($value);
                    294: 		$new_value = &update_value($new_value);
                    295: 		$my_db->{$new_key} = &escape($new_value);
                    296: 	    }
                    297: 	}
                    298:     } elsif ($type eq 'slots') {
                    299: 	foreach my $key (keys(%{$my_db})) {
                    300: 	    my $value = $my_db->{$key};
                    301: 	    $value = &Apache::lonnet::thaw_unescape($value);
                    302: 	    if (exists($value->{'proctor'})) {
                    303: 		$value->{'proctor'} = &update_value($value->{'proctor'});
                    304: 	    }
                    305: 	    if (exists($value->{'allowedusers'})) {
                    306: 		$value->{'allowedusers'} = 
                    307: 		    &update_value($value->{'allowedusers'});
                    308: 	    }
                    309: 	    $my_db->{$key} = &Apache::lonnet::freeze_escape($value);
                    310: 	}
                    311:     } elsif ($type eq 'reservation') {
                    312: 	foreach my $key (keys(%{$my_db})) {
                    313: 	    my $value = $my_db->{$key};
                    314: 	    $value = &Apache::lonnet::thaw_unescape($value);
                    315: 	    if (exists($value->{'name'})) {
                    316: 		$value->{'name'} = &update_value($value->{'name'});
                    317: 	    }
                    318: 	    $my_db->{$key} = &Apache::lonnet::freeze_escape($value);
                    319: 	}
                    320:     }
                    321: }
                    322: 
                    323: sub update_value {
                    324:     my ($value) = @_;
                    325:     if ($value =~ /@/ && $value !~ /:/) {
                    326: 	$value =~ tr/@/:/;
                    327:     }
                    328:     return $value;
                    329: }

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