Diff for /loncom/metadata_database/parse_activity_log.pl between versions 1.8 and 1.14

version 1.8, 2004/12/20 19:53:36 version 1.14, 2005/03/31 22:09:40
Line 34 Line 34
 #   2     Activity log does not exist  #   2     Activity log does not exist
 #   3     Unable to connect to database  #   3     Unable to connect to database
 #   4     Unable to create database tables  #   4     Unable to create database tables
 #   5     Unspecified error?  #   5     Unable to open log file
   #   6     Unable to get lock on activity log
 #  #
   
 #  #
Line 50 Line 51
   
 use strict;  use strict;
 use DBI;  use DBI;
   use lib '/home/httpd/lib/perl/Apache';
 use lib '/home/httpd/lib/perl/';  use lib '/home/httpd/lib/perl/';
 use LONCAPA::Configuration();  use LONCAPA::Configuration();
 use Apache::lonmysql();  use Apache::lonmysql();
Line 59  use Getopt::Long(); Line 61  use Getopt::Long();
 use IO::File;  use IO::File;
 use File::Copy;  use File::Copy;
 use Fcntl qw(:flock);  use Fcntl qw(:flock);
   use HTML::TokeParser;
   
 #  #
 # Determine parameters  # Determine parameters
 my ($help,$course,$domain,$drop,$file,$time_run,$nocleanup,$log,$backup);  my ($help,$course,$domain,$drop_when_done,$srcfile,$logfile,$time_run,$nocleanup,$log,$backup);
 &Getopt::Long::GetOptions( "course=s"  => \$course,  &Getopt::Long::GetOptions( "course=s"  => \$course,
                            "domain=s"  => \$domain,                             "domain=s"  => \$domain,
                            "backup"    => \$backup,                             "backup"    => \$backup,
                            "help"      => \$help,                             "help"      => \$help,
                            "logfile=s" => \$file,                             "logfile=s" => \$logfile,
                              "srcfile=s" => \$srcfile,
                            "timerun"   => \$time_run,                             "timerun"   => \$time_run,
                            "nocleanup" => \$nocleanup,                             "nocleanup" => \$nocleanup,
                            "drop"      => \$drop,                             "dropwhendone" => \$drop_when_done,
                            "log"       => \$log);                             "log"       => \$log);
 if (! defined($course) || $help) {  if (! defined($course) || $help) {
     print<<USAGE;      print<<USAGE;
Line 79  parse_activity_log.pl Line 83  parse_activity_log.pl
 Process a lon-capa activity log into a database.  Process a lon-capa activity log into a database.
 Parameters:  Parameters:
    course             Required     course             Required
    domain             Optional     domain             optional
    backup             optional   if present, backup the activity log file     backup             optional   if present, backup the activity log file
                                  before processing it                                   before processing it
    drop               optional   if present, drop all course      dropwhendone       optional   if present, drop all course 
                                  specific activity log tables.                                   specific activity log tables after processing.
    file               optional   Specify the file to parse, including path     srcfile            optional   Specify the file to parse, including path
    time               optional   if present, print out timing data     time               optional   if present, print out timing data
    nocleanup          optional   if present, do not remove old files     nocleanup          optional   if present, do not remove old files
    log                optional   if present, prepare log file of activity     log                optional   if present, prepare log file of activity
      logfile            optional   specifies the logfile to use
 Examples:  Examples:
   $0 -course=123456abcdef -domain=msu    $0 -course=123456abcdef -domain=msu
   $0 -course=123456abcdef -file=activity.log    $0 -course=123456abcdef -srcfile=activity.log
     $0 -course-123456abcdef -log -logfile=/tmp/logfile -dropwhendone
 USAGE  USAGE
     exit;      exit;
 }  }
Line 116  if (! defined($domain) || $domain eq '') Line 122  if (! defined($domain) || $domain eq '')
 ##  ##
 ## Set up logging code  ## Set up logging code
 my $logthis = \&nothing;  my $logthis = \&nothing;
   
 if ($log) {  if ($log) {
     my $logfile = $perlvar{'lonDaemons'}.'/tmp/parse_activity_log.log.'.time;      if (! $logfile) {
           $logfile = $perlvar{'lonDaemons'}.'/tmp/parse_activity_log.log.'.time;
       }
     print STDERR "$0: logging to $logfile".$/;      print STDERR "$0: logging to $logfile".$/;
     if (! open(LOGFILE,">$logfile")) {      if (! open(LOGFILE,">$logfile")) {
         die "Unable to open $logfile for writing.  Run aborted.";          warn("Unable to open $logfile for writing.  Run aborted.");
           exit 5;
     } else {      } else {
         $logthis = \&log_to_file;          $logthis = \&log_to_file;
     }      }
Line 132  if ($log) { Line 142  if ($log) {
 ##  ##
 my $sourcefilename;   # activity log data  my $sourcefilename;   # activity log data
 my $newfilename;      # $sourcefilename will be renamed to this  my $newfilename;      # $sourcefilename will be renamed to this
 my $gz_sql_filename;  # the gzipped mysql backup data file name.  
 my $error_filename;   # Errors in parsing the activity log will be written here  my $error_filename;   # Errors in parsing the activity log will be written here
 if ($file) {  if ($srcfile) {
     $sourcefilename = $file;      $sourcefilename = $srcfile;
 } else {  } else {
     $sourcefilename = &get_filename($course,$domain);      $sourcefilename = &get_filename($course,$domain);
 }  }
 my $sql_filename = $sourcefilename;  my $sql_filename = $sourcefilename;
 $sql_filename =~ s|[^/]*$|activity.log.sql|;  $sql_filename =~ s|[^/]*$|activity.log.sql|;
 $gz_sql_filename = $sql_filename.'.gz';  my $gz_sql_filename = $sql_filename.'.gz';
   #
   my $xml_filename = $sourcefilename;
   $xml_filename =~ s|[^/]*$|activity.log.xml|;
   my $gz_xml_filename = $xml_filename.'.gz';
   #
 $error_filename = $sourcefilename;  $error_filename = $sourcefilename;
 $error_filename =~ s|[^/]*$|activity.log.errors|;  $error_filename =~ s|[^/]*$|activity.log.errors|;
 $logthis->('Beginning logging '.time);  $logthis->('Beginning logging '.time);
   
   
 #  #
 # Wait for a lock on the lockfile to avoid collisions  # Wait for a lock on the lockfile to avoid collisions
 my $lockfilename = $sourcefilename.'.lock';  my $lockfilename = $sourcefilename.'.lock';
 open(LOCKFILE,'>'.$lockfilename);  open(LOCKFILE,'>'.$lockfilename);
 flock(LOCKFILE,LOCK_EX) || die("Unable to lock $lockfilename.  Aborting".$/);  if (!flock(LOCKFILE,LOCK_EX)) {
       warn("Unable to lock $lockfilename.  Aborting".$/);
       exit 6;
   }
   
 ##  ##
 ## There will only be a $newfilename file if a copy of this program is already  ## There will only be a $newfilename file if a copy of this program is already
Line 180  close(LOCKFILE); Line 196  close(LOCKFILE);
 ##  ##
 ## Table definitions  ## Table definitions
 ##  ##
 my $prefix = $course.'_'.$domain.'_';  my %tables = &table_names($course,$domain);
 my $student_table = $prefix.'students';  
 my $student_table_def =   my $student_table_def = 
 { id => $student_table,  { id => $tables{'student'},
   permanent => 'no',    permanent => 'no',
   columns => [    columns => [
               { name => 'student_id',                { name => 'student_id',
Line 197  my $student_table_def = Line 212  my $student_table_def =
       'PRIMARY KEY' => ['student_id',],        'PRIMARY KEY' => ['student_id',],
           };            };
   
 my $res_table = $prefix.'resource';  
 my $res_table_def =   my $res_table_def = 
 { id => $res_table,  { id => $tables{'res'},
   permanent => 'no',    permanent => 'no',
   columns => [{ name => 'res_id',    columns => [{ name => 'res_id',
                 type => 'MEDIUMINT UNSIGNED',                  type => 'MEDIUMINT UNSIGNED',
Line 212  my $res_table_def = Line 226  my $res_table_def =
   'PRIMARY KEY' => ['res_id'],    'PRIMARY KEY' => ['res_id'],
 };  };
   
 #my $action_table = $prefix.'actions';  
 #my $action_table_def =  #my $action_table_def =
 #{ id => $action_table,  #{ id => $action_table,
 #  permanent => 'no',  #  permanent => 'no',
Line 227  my $res_table_def = Line 240  my $res_table_def =
 #  'PRIMARY KEY' => ['action_id',],   #  'PRIMARY KEY' => ['action_id',], 
 #};  #};
   
 my $machine_table = $prefix.'machine_table';  
 my $machine_table_def =  my $machine_table_def =
 { id => $machine_table,  { id => $tables{'machine'},
   permanent => 'no',    permanent => 'no',
   columns => [{ name => 'machine_id',    columns => [{ name => 'machine_id',
                 type => 'MEDIUMINT UNSIGNED',                  type => 'MEDIUMINT UNSIGNED',
Line 242  my $machine_table_def = Line 254  my $machine_table_def =
   'PRIMARY KEY' => ['machine_id',],    'PRIMARY KEY' => ['machine_id',],
  };   };
   
 my $activity_table = $prefix.'activity';  
 my $activity_table_def =   my $activity_table_def = 
 { id => $activity_table,  { id => $tables{'activity'},
   permanent => 'no',    permanent => 'no',
   columns => [    columns => [
               { name => 'res_id',                { name => 'res_id',
Line 276  my $activity_table_def = Line 287  my $activity_table_def =
   
 my @Activity_Table = ($activity_table_def);  my @Activity_Table = ($activity_table_def);
 my @ID_Tables = ($student_table_def,$res_table_def,$machine_table_def);  my @ID_Tables = ($student_table_def,$res_table_def,$machine_table_def);
                  
 ##  ##
 ## End of table definitions  ## End of table definitions
 ##  ##
   $logthis->('tables = '.join(',',keys(%tables)));
   
 $logthis->('Connectiong to mysql');  $logthis->('Connectiong to mysql');
 &Apache::lonmysql::set_mysql_user_and_password('www',  &Apache::lonmysql::set_mysql_user_and_password('www',
Line 290  if (!&Apache::lonmysql::verify_sql_conne Line 303  if (!&Apache::lonmysql::verify_sql_conne
 }  }
 $logthis->('SQL connection is up');  $logthis->('SQL connection is up');
   
 if ($drop) { &drop_tables(); $logthis->('dropped tables'); }  my $missing_table = &check_for_missing_tables(values(%tables));
   if (-s $gz_sql_filename && ! -s $gz_xml_filename) {
 if (-s $gz_sql_filename) {  
     my $backup_modification_time = (stat($gz_sql_filename))[9];      my $backup_modification_time = (stat($gz_sql_filename))[9];
     $logthis->($gz_sql_filename.' was last modified '.      $logthis->($gz_sql_filename.' was last modified '.
                localtime($backup_modification_time).                 localtime($backup_modification_time).
                '('.$backup_modification_time.')');                 '('.$backup_modification_time.')');
     # Check for missing tables      if ($missing_table) {
     my @Current_Tables = &Apache::lonmysql::tables_in_db();          # If the backup happened prior to the last table modification,
     $logthis->(join(',',@Current_Tables));          # we need to save the tables.
     my %Found;          if (&latest_table_modification_time() > $backup_modification_time) {
     foreach my $tablename (@Current_Tables) {              # Save the current tables in case we need them another time.
         foreach my $table (@Activity_Table,@ID_Tables) {              $logthis->('Backing existing tables up');
             if ($tablename eq  $table->{'id'}) {              &backup_tables_as_xml($gz_xml_filename.'.save_'.time,\%tables);
                 $Found{$tablename}++;  
             }  
         }  
     }  
     $logthis->('Found tables '.join(',',keys(%Found)));  
     my $missing_a_table = 0;  
     foreach my $table (@Activity_Table,@ID_Tables) {      
         # Hmmm, should I dump the tables?  
         if (! $Found{$table->{'id'}}) {  
             $logthis->('Missing table '.$table->{'id'});  
             $missing_a_table = 1;  
             last;  
         }          }
           $time_this->();
           &load_backup_sql_tables($gz_sql_filename);
           &backup_tables_as_xml($gz_xml_filename,\%tables);
           $time_this->('load backup tables');
     }      }
     if ($missing_a_table) {  } elsif (-s $gz_xml_filename) {
       my $backup_modification_time = (stat($gz_xml_filename))[9];
       $logthis->($gz_xml_filename.' was last modified '.
                  localtime($backup_modification_time).
                  '('.$backup_modification_time.')');
       if ($missing_table) {
         my $table_modification_time = $backup_modification_time;          my $table_modification_time = $backup_modification_time;
         # If the backup happened prior to the last table modification,          # If the backup happened prior to the last table modification,
         foreach my $table (@Activity_Table,@ID_Tables) {              # we need to save the tables.
             my %tabledata = &Apache::lonmysql::table_information($table->{'id'});          if (&latest_table_modification_time() > $backup_modification_time) {
             next if (! scalar(keys(%tabledata))); # table does not exist  
             if ($table_modification_time < $tabledata{'Update_time'}) {  
                 $table_modification_time = $tabledata{'Update_time'};  
             }  
         }  
         $logthis->("Table modification time = ".$table_modification_time);  
         if ($table_modification_time > $backup_modification_time) {  
             # Save the current tables in case we need them another time.              # Save the current tables in case we need them another time.
             my $backup_name = $gz_sql_filename.'.'.time;              $logthis->('Backing existing tables up');
             $logthis->('Backing existing tables up in '.$backup_name);              &backup_tables_as_xml($gz_xml_filename.'.save_'.time,\%tables);
             &backup_tables($backup_name);  
         }          }
         $time_this->();          $time_this->();
         &load_backup_tables($gz_sql_filename);          # We have to make our own tables for the xml format
           &drop_tables();
           &create_tables();
           &load_backup_xml_tables($gz_xml_filename,\%tables);
         $time_this->('load backup tables');          $time_this->('load backup tables');
     }      }    
 }  }
   
 ##  ##
Line 365  my $error_fh = IO::File->new(">>$error_f Line 369  my $error_fh = IO::File->new(">>$error_f
 ## Parse the course log  ## Parse the course log
 $logthis->('processing course log');  $logthis->('processing course log');
 if (-s $newfilename) {  if (-s $newfilename) {
     my $result = &process_courselog($newfilename,$error_fh);      my $result = &process_courselog($newfilename,$error_fh,\%tables);
     if (! defined($result)) {      if (! defined($result)) {
         # Something went wrong along the way...          # Something went wrong along the way...
         $logthis->('process_courselog returned undef');          $logthis->('process_courselog returned undef');
Line 373  if (-s $newfilename) { Line 377  if (-s $newfilename) {
     } elsif ($result > 0) {      } elsif ($result > 0) {
         $time_this->();          $time_this->();
         $logthis->('process_courselog returned '.$result.' backing up tables');          $logthis->('process_courselog returned '.$result.' backing up tables');
         &backup_tables($gz_sql_filename);          &backup_tables_as_xml($gz_xml_filename,\%tables);
         $time_this->('write backup tables');          $time_this->('write backup tables');
     }      }
       if ($drop_when_done) { &drop_tables(); $logthis->('dropped tables'); }
 }  }
 close($error_fh);  close($error_fh);
   
Line 399  if ($log) { Line 404  if ($log) {
     close LOGFILE;      close LOGFILE;
 }  }
   
   foreach my $file ($lockfilename, $error_filename,$logfile) {
       if (-z $file) { 
           unlink($file); 
       }
   }
   
 exit 0;   # Everything is okay, so end here before it gets worse.  exit 0;   # Everything is okay, so end here before it gets worse.
   
 ########################################################  ########################################################
 ########################################################  ########################################################
   sub table_names {
       my ($course,$domain) = @_;
       my $prefix = $course.'_'.$domain.'_';
       #
       my %tables = 
           ( student =>&Apache::lonmysql::fix_table_name($prefix.'students'),
             res     =>&Apache::lonmysql::fix_table_name($prefix.'resource'),
             machine =>&Apache::lonmysql::fix_table_name($prefix.'machine_table'),
             activity=>&Apache::lonmysql::fix_table_name($prefix.'activity'),
             );
       return %tables;
   }
   
   ########################################################
   ########################################################
 ##  ##
 ##                 Process Course Log  ##                 Process Course Log
 ##  ##
Line 411  exit 0;   # Everything is okay, so end h Line 437  exit 0;   # Everything is okay, so end h
 #  #
 # Returns the number of lines in the activity.log file that were processed.  # Returns the number of lines in the activity.log file that were processed.
 sub process_courselog {  sub process_courselog {
     my ($inputfile,$error_fh) = @_;      my ($inputfile,$error_fh,$tables) = @_;
     if (! open(IN,$inputfile)) {      if (! open(IN,$inputfile)) {
         warn "Unable to open '$inputfile' for reading";          warn "Unable to open '$inputfile' for reading";
         $logthis->("Unable to open '$inputfile' for reading");          $logthis->("Unable to open '$inputfile' for reading");
Line 420  sub process_courselog { Line 446  sub process_courselog {
     my ($linecount,$insertcount);      my ($linecount,$insertcount);
     my $dbh = &Apache::lonmysql::get_dbh();      my $dbh = &Apache::lonmysql::get_dbh();
     #      #
     # Timing variables      &store_entry();
     my @RowData;  
     while (my $line=<IN>){      while (my $line=<IN>){
         # last if ($linecount > 1000);          # last if ($linecount > 1000);
         #          #
Line 431  sub process_courselog { Line 456  sub process_courselog {
         $linecount++;          $linecount++;
         # print $linecount++.$/;          # print $linecount++.$/;
         my ($timestamp,$host,$log)=split(/\:/,$line,3);          my ($timestamp,$host,$log)=split(/\:/,$line,3);
         $time_this->('splitline');  
         #          #
         # $log has the actual log entries; currently still escaped, and          # $log has the actual log entries; currently still escaped, and
         # %26(timestamp)%3a(url)%3a(user)%3a(domain)          # %26(timestamp)%3a(url)%3a(user)%3a(domain)
Line 443  sub process_courselog { Line 467  sub process_courselog {
         # get delimiter between timestamped entries to be &&&          # get delimiter between timestamped entries to be &&&
         $log=~s/\%26(\d{9,10})\%3a/\&\&\&$1\%3a/g;          $log=~s/\%26(\d{9,10})\%3a/\&\&\&$1\%3a/g;
         $log = &unescape($log);          $log = &unescape($log);
         $time_this->('translate_and_unescape');  
         # now go over all log entries           # now go over all log entries 
         if (! defined($host)) { $host = 'unknown'; }          if (! defined($host)) { $host = 'unknown'; }
         my $machine_id = &get_id($machine_table,'machine',$host);  
         my $prevchunk = 'none';          my $prevchunk = 'none';
         foreach my $chunk (split(/\&\&\&/,$log)) {          foreach my $chunk (split(/\&\&\&/,$log)) {
             my $warningflag = '';              my $warningflag = '';
             $time_this->();  
     my ($time,$res,$uname,$udom,$action,@values)= split(/:/,$chunk);      my ($time,$res,$uname,$udom,$action,@values)= split(/:/,$chunk);
             my $student = $uname.':'.$udom;  
             if (! defined($res) || $res =~ /^\s*$/) {              if (! defined($res) || $res =~ /^\s*$/) {
                 $res = '/adm/roles';                  $res = '/adm/roles';
                 $action = 'LOGIN';                  $action = 'LOGIN';
Line 474  sub process_courselog { Line 494  sub process_courselog {
                          'uname= '.$uname.$/.                           'uname= '.$uname.$/.
                          'udom = '.$udom.$/.                           'udom = '.$udom.$/.
                          'action='.$action.$/.                           'action='.$action.$/.
                          '@values = '.join(':',@values));                           '@values = '.join('&',@values));
                 next; #skip it if we cannot understand what is happening.                  next; #skip it if we cannot understand what is happening.
             }              }
             if (! defined($student) || $student eq ':') {  
                 $student = 'unknown';  
                 $warningflag .= 'student';  
             }  
             if (! defined($res) || $res =~ /^\s*$/) {  
                 $res = 'unknown';  
                 $warningflag .= 'res';  
             }  
             if (! defined($action) || $action =~ /^\s*$/) {  
                 $action = 'unknown';  
                 $warningflag .= 'action';  
             }  
             if (! defined($time) || $time !~ /^\d+$/) {  
                 $time = 0;  
                 $warningflag .= 'time';  
             }  
             #  
             $time_this->('split_and_error_check');  
             my $student_id = &get_id($student_table,'student',$student);  
             my $res_id     = &get_id($res_table,'resource',$res);  
 #            my $action_id  = &get_id($action_table,'action',$action);  
             my $sql_time   = &Apache::lonmysql::sqltime($time);  
             #              #
             if (! defined($student_id) || $student_id eq '') {               my %data = (student  => $uname.':'.$udom,
                 $warningflag.='student_id';                           resource => $res,
             }                          machine  => $host,
             if (! defined($res_id) || $res_id eq '') {                           action   => $action,
                 $warningflag.='res_id';                           time => &Apache::lonmysql::sqltime($time));
             }              if ($action eq 'POST') {
 #            if (! defined($action_id) || $action_id eq '') {                   $data{'action_values'} =
 #                $warningflag.='action_id';                       $dbh->quote(join('&',map { &escape($_); } @values));
 #            }              } else {
             if ($warningflag ne '') {                  $data{'action_values'} = $dbh->quote(join('&',@values));
                 print $error_fh 'full log entry:'.$log.$/;              }
                 print $error_fh 'error on chunk:'.$chunk.$/;              my $error = &store_entry($dbh,$tables,\%data);
                 $logthis->('warningflag ('.$warningflag.') on chunk '.              if ($error) {
                            $/.$chunk.$/.'prevchunk = '.$/.$prevchunk);                  $logthis->('error store_entry:'.$error." on %data");
                 $prevchunk .= $chunk;  
                 next; # skip this chunk  
             }              }
             #  
             my $values = $dbh->quote(join('',@values));  
             $time_this->('get_ids');  
             #  
             my $row = [$res_id,  
                        qq{'$sql_time'},  
                        $student_id,  
                        "'".$action."'",  
 #                       $action_id,  
                        qq{''},        # idx  
                        $machine_id,  
                        $values];  
             push(@RowData,$row);  
             $time_this->('push_row');  
             $prevchunk = $chunk;              $prevchunk = $chunk;
             #  
         }  
         $time_this->();  
         if ((scalar(@RowData) > 0) && ($linecount % 100 == 0)) {  
             my $result = &Apache::lonmysql::bulk_store_rows($activity_table,  
                                                             undef,  
                                                             \@RowData);  
             # $logthis->('result = '.$result);  
             $time_this->('bulk_store_rows');  
             if (! defined($result)) {  
                 my $error = &Apache::lonmysql::get_error();  
                 warn "Error occured during insert.".$error;  
                 $logthis->('error = '.$error);  
             }  
             undef(@RowData);  
         }          }
     }      }
     if (@RowData) {      my $result = &store_entry($dbh,$tables);
         $time_this->();      if (! defined($result)) {
         $logthis->('storing '.$linecount);          my $error = &Apache::lonmysql::get_error();
         my $result = &Apache::lonmysql::bulk_store_rows($activity_table,          warn "Error occured during insert.".$error;
                                                         undef,          $logthis->('error = '.$error);
                                                         \@RowData);  
         $logthis->('result = '.$result);  
         $time_this->('bulk_store_rows');  
         if (! defined($result)) {  
             my $error = &Apache::lonmysql::get_error();  
             warn "Error occured during insert.".$error;  
             $logthis->('error = '.$error);  
         }  
         undef(@RowData);  
     }      }
     close IN;      close IN;
 #    print "Number of lines: ".$linecount.$/;  
 #    print "Number of inserts: ".$insertcount.$/;  
     return $linecount;      return $linecount;
 }  }
   
   
 ##  ##
 ## Somtimes, instead of doing something, doing nothing is appropriate.  ## default value for $logthis and $time_this
 sub nothing {  sub nothing {
     return;      return;
 }  }
   
 ##  ##
 ## Logging routine  ## Logging routine (look for $log)
 ##  ##
 sub log_to_file {  sub log_to_file {
     my ($input)=@_;      my ($input)=@_;
Line 619  sub outputtimes { Line 575  sub outputtimes {
   
 }  }
   
   sub latest_table_modification_time {
       my $latest_time;
       foreach my $table (@Activity_Table,@ID_Tables) {    
           my %tabledata = &Apache::lonmysql::table_information($table->{'id'});
           next if (! scalar(keys(%tabledata))); # table does not exist
           if (! defined($latest_time) ||
               $latest_time < $tabledata{'Update_time'}) {
               $latest_time = $tabledata{'Update_time'};
           }
       }
       return $latest_time;
   }
   
   sub check_for_missing_tables {
       my @wanted_tables = @_;
       # Check for missing tables
       my @Current_Tables = &Apache::lonmysql::tables_in_db();
       my %Found;
       foreach my $tablename (@Current_Tables) {
           foreach my $table (@wanted_tables) {
               if ($tablename eq  $table) {
                   $Found{$tablename}++;
               }
           }
       }
       $logthis->('Found tables '.join(',',keys(%Found)));
       my $missing_a_table = 0;
       foreach my $table (@wanted_tables) {
           if (! $Found{$table}) {
               $logthis->('Missing table '.$table);
               $missing_a_table = 1;
               last;
           }
       }
       return $missing_a_table;
   }
   
 ##  ##
 ## Use mysqldump to store backups of the tables  ## Use mysqldump to store backups of the tables
 ##  ##
 sub backup_tables {  sub backup_tables_as_sql {
     my ($gz_sql_filename) = @_;      my ($gz_sql_filename) = @_;
     my $command = qq{mysqldump --opt loncapa };      my $command = qq{mysqldump --quote-names --opt loncapa };
                                
     foreach my $table (@ID_Tables,@Activity_Table) {      foreach my $table (@ID_Tables,@Activity_Table) {
         my $tablename = $table->{'id'};          my $tablename = $table->{'id'};
           $tablename =~ s/\`//g;
         $command .= $tablename.' ';          $command .= $tablename.' ';
     }      }
     $command .= '| gzip >'.$gz_sql_filename;      $command .= '| gzip >'.$gz_sql_filename;
Line 639  sub backup_tables { Line 631  sub backup_tables {
 ##  ##
 ## Load in mysqldumped files  ## Load in mysqldumped files
 ##  ##
 sub load_backup_tables {  sub load_backup_sql_tables {
     my ($gz_sql_filename) = @_;      my ($gz_sql_filename) = @_;
     if (-s $gz_sql_filename) {      if (-s $gz_sql_filename) {
         $logthis->('loading data from gzipped sql file');          $logthis->('loading data from gzipped sql file');
Line 668  sub get_filename { Line 660  sub get_filename {
 sub create_tables {  sub create_tables {
     foreach my $table (@ID_Tables,@Activity_Table) {      foreach my $table (@ID_Tables,@Activity_Table) {
         my $table_id = &Apache::lonmysql::create_table($table);          my $table_id = &Apache::lonmysql::create_table($table);
 #        print STDERR "Unable to create table ".$table->{'id'}.$/;  
 #        print STDERR join($/,&Apache::lonmysql::build_table_creation_request($table)).$/;  
         if (! defined($table_id)) {          if (! defined($table_id)) {
             warn "Unable to create table ".$table->{'id'}.$/;              warn "Unable to create table ".$table->{'id'}.$/;
             warn join($/,&Apache::lonmysql::build_table_creation_request($table)).$/;              $logthis->('Unable to create table '.$table->{'id'});
               $logthis->(join($/,&Apache::lonmysql::build_table_creation_request($table)));
             return 0;              return 0;
         }          }
     }      }
Line 733  sub get_id { Line 724  sub get_id {
   
 } # End of ID scoping  } # End of ID scoping
   
   ###############################################################
   ###############################################################
   ##
   ##   Save as XML
   ##
   ###############################################################
   ###############################################################
   sub backup_tables_as_xml {
       my ($filename,$tables) = @_;
       open(XMLFILE,"|gzip - > $filename") || return ('error:unable to write '.$filename);
       my $query = qq{
           SELECT B.resource,
                  A.time,
                  A.idx,
                  C.student,
                  A.action,
                  E.machine,
                  A.action_values 
               FROM $tables->{'activity'} AS A
               LEFT JOIN $tables->{'res'}      AS B ON B.res_id=A.res_id 
               LEFT JOIN $tables->{'student'}  AS C ON C.student_id=A.student_id 
               LEFT JOIN $tables->{'machine'}  AS E ON E.machine_id=A.machine_id
               ORDER BY A.time DESC
           };
       $query =~ s/\s+/ /g;
       my $dbh = &Apache::lonmysql::get_dbh();
       my $sth = $dbh->prepare($query);
       if (! $sth->execute()) {
           $logthis->('<font color="blue">'.
                      'WARNING: Could not retrieve from database:'.
                      $sth->errstr().'</font>');
           return undef;
       } else {
           my ($res,$sqltime,$idx,$student,$action,$machine,$action_values);
           if ($sth->bind_columns(\$res,\$sqltime,\$idx,\$student,\$action,
                                  \$machine,\$action_values)) {
               
               while ($sth->fetch) {
                   print XMLFILE '<row>'.
                       qq{<resource>$res</resource>}.
                       qq{<time>$sqltime</time>}.
                       qq{<idx>$idx</idx>}.
                       qq{<student>$student</student>}.
                       qq{<action>$action</action>}.
                       qq{<machine>$machine</machine>}.
                       qq{<action_values>$action_values</action_values>}.
                       '</row>'.$/;
               }
           } else {
               warn "Unable to bind to columns.\n";
               return undef;
           }
       }
       close XMLFILE;
       return;
   }
   
   ###############################################################
   ###############################################################
   ##
   ##   load as xml
   ##
   ###############################################################
   ###############################################################
   sub load_backup_xml_tables {
       my ($filename,$tables) = @_;
       my $xmlfh;
       open($xmlfh,"cat $filename | gzip -d - |");
       if (! defined($xmlfh)) {
           return ('error:unable to read '.$filename);
       }
       my $dbh = &Apache::lonmysql::get_dbh();
       my $parser = HTML::TokeParser->new($xmlfh);
       $parser->xml_mode('1');
       &store_entry();
       my %data;
       while (my $token = $parser->get_token()) {
           if ($token->[0] eq 'S' && $token->[1] eq 'row') {
               undef(%data);
           }
           foreach my $tag ('resource','time','idx',
                            'student','action','machine','action_values') {
               if ($token->[0] eq 'S' && $token->[1] eq $tag) {
                   my $text = $parser->get_text("/$tag");
                   $data{$tag} = $text;
               }
           }
           if ($token->[0] eq 'E' && $token->[1] eq 'row') {
               $data{'action_values'} =qq{'$data{'action_values'}'};
               my $error = &store_entry($dbh,$tables,\%data);
           }
       }
       &store_entry($dbh,$tables);
       return;
   }
   
   
   #######################################################################
   #######################################################################
   ##
   ## store_entry - accumulate data to be inserted into the database
   ## 
   ## Pass no values in to clear accumulator
   ## Pass ($dbh,\%tables) to initiate storage of values
   ## Pass ($dbh,\%tables,\%data) to use normally
   ##
   #######################################################################
   #######################################################################
   
   {
       my @rows;
       my $max_row_count = 100;
   
   sub store_entry {
       if (! @_) {
           undef(@rows);
           return '';
       }
       my ($dbh,$tables,$data) = @_;
       return if (! defined($tables));
       if (defined($data)) {
           my $error;
           foreach my $field ('student','resource','action','time') {
               if (! defined($data->{$field}) || $data->{$field} eq ':' ||
                   $data->{$field}=~ /^\s*$/) {
                   $error.=$field.',';
               }
           }
           if ($error) { $error=~s/,$//; return $error; }
           #
           my $student_id = &get_id($tables->{'student'},'student',
                                    $data->{'student'});
           my $res_id     = &get_id($tables->{'res'},
                                    'resource',$data->{'resource'});
           my $machine_id = &get_id($tables->{'machine'},
                                    'machine',$data->{'machine'});
           my $idx = $data->{'idx'}; if (! $idx) { $idx = "''"; }
           #
           push(@rows,[$res_id,
                       qq{'$data->{'time'}'},
                       $student_id,
                       qq{'$data->{'action'}'},
                       $idx,
                       $machine_id,
                       $data->{'action_values'}]);
       }
       if (defined($tables) && 
           ( (! defined($data) && scalar(@rows)) || scalar(@rows)>$max_row_count)
           ){
           # Store the rows
           my $result = 
               &Apache::lonmysql::bulk_store_rows($tables->{'activity'},
                                                  undef,
                                                  \@rows);
           if (! defined($result)) {
               my $error = &Apache::lonmysql::get_error();
               warn "Error occured during insert.".$error;
               return $error;
           }
           undef(@rows);
           return $result if (! defined($data));
       }
   
       return '';
   }
   
   } # end of scope for &store_entry
   
 ###############################################################  ###############################################################
 ###############################################################  ###############################################################

Removed from v.1.8  
changed lines
  Added in v.1.14


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