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

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.5     ! albertel    7: # $Id: rebuild_db_from_hist.pl,v 1.4 2004/12/09 22:25:47 matthew 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.4       matthew    41: my ($help,$debug,$test,$p_is_s);
1.1       matthew    42: GetOptions("help"    => \$help,
                     43:            "debug"   => \$debug,
1.4       matthew    44:            "test"    => \$test,
                     45:            "p_is_s"  => \$p_is_s);
1.1       matthew    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.
1.2       matthew    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.
1.4       matthew    60:    -p_is_s   Treat 'P' lines as 'S' lines.
1.1       matthew    61: Examples: 
1.2       matthew    62:     rebuild_db_from_hist.pl -t $file.hist  # Perform a test rebuild
                     63:     rebuild_db_from_hist.pl $file.hist       
1.1       matthew    64: END
                     65:     exit;
                     66: }
                     67: 
                     68: #
                     69: # Loop through ARGV getting files.
                     70: while (my $fname = shift) {
1.5     ! albertel   71:     if ($fname !~ m/\.hist$/) {
        !            72: 	print("error: $fname is not a hist file");
        !            73: 	next;
        !            74:     }
        !            75: 
1.1       matthew    76:     my $db_filename = $fname;
                     77:     $db_filename =~ s/\.hist$/\.db/;
                     78:     if (-e $db_filename && ! $test) {
1.5     ! albertel   79:         print "Aborting: The target file $db_filename exists.".$/;
1.1       matthew    80:         next;
                     81:     }
1.3       matthew    82:     my ($error,$constructed_hash) = &process_file($fname,$db_filename,$debug);
1.5     ! albertel   83:     if (! defined($error) ) {
        !            84: 	$error = &update_hash($db_filename,$constructed_hash);
        !            85:     }
1.1       matthew    86:     if (! defined($error) || ! $test) {
                     87:         $error = &write_hash($db_filename,$constructed_hash);
                     88:     }
                     89:     if ($test) {
1.5     ! albertel   90:         $error = &write_hash($db_filename.'.test',$constructed_hash);
        !            91:     }
        !            92:     if ($test) {
1.1       matthew    93:         my $error = &test_hash($db_filename,$constructed_hash);
                     94:         if (defined($error)) {
                     95:             print "Error processing ".$fname.$/;
1.5     ! albertel   96:             print $error;
1.1       matthew    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
1.5     ! albertel  121:         #  S:store
1.1       matthew   122:         #  D:delete
1.5     ! albertel  123:         #  N:new put (only adds tha values if they are all new values)
1.1       matthew   124:         my ($action,$time,$concatenated_data) = split(':',$command,3);
1.3       matthew   125:         if ($fname eq 'roles.hist' && $concatenated_data =~ /^.*:.*:/) {
                    126:             (undef,undef,$concatenated_data) = split(':',$concatenated_data,3);
                    127:         }
1.4       matthew   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"};
1.5     ! albertel  134:              #print $version.$/;
1.4       matthew   135:         }
                    136:         next if (! defined($concatenated_data));
1.5     ! albertel  137: 	my $add_new_data = 1;
1.1       matthew   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:                 }
1.4       matthew   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;
1.5     ! albertel  151:             } elsif ($action eq 'N') {
        !           152:                 if (exists($db_to_store{$key})) {
        !           153: 		    $add_new_data = 0;
        !           154: 		    print "exists $key\n";
        !           155: 		}
1.1       matthew   156:             } elsif ($action eq 'D') {
                    157:                 delete($db_to_store{$key});
                    158:             } else {
                    159:                 $error = "Unable to understand action '".$action."'";
                    160:             }
                    161:         }
1.5     ! albertel  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: 	}
1.4       matthew   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:         }
1.1       matthew   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) {
1.4       matthew   226:         $error.=$extra_count.' extra key/value pairs found in hist: '.$/;
1.1       matthew   227:         while (my ($k,$v) = each(%$my_db)) {
1.5     ! albertel  228: 	    $error .= '  "'.$k.'" => "'.$v.'"'.$/;
1.1       matthew   229:         }
                    230:     }
                    231:     my $key_count = scalar(keys(%key_errors));
                    232:     if ($key_count) {
1.4       matthew   233:         $error.=$key_count.' missing keys found in db but not in hist: '.$/;
1.1       matthew   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) {
1.4       matthew   240:         $error.=$value_count.' mismatched values found: '.$/;
1.1       matthew   241:         while (my ($k,$v) = each(%value_errors)) {
                    242:             $error .= '  "'.$k.'" => "'.$v.'"'.$/;
                    243:         }
                    244:     }
                    245:     #
                    246:     return $error;
                    247: }
1.5     ! albertel  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>