Ticket #275: fuzzy-stats_tlyons.mysql.patch

File fuzzy-stats_tlyons.mysql.patch, 7.5 kB (added by mrballcb, 2 years ago)

Adds mysql support to fuzzy-stats (includes previous colon patch)

  • fuzzy-stats

    old new  
    1 #!/usr/local/bin/perl 
     1#!/usr/bin/perl 
    22#----- 
    33 
     4use strict; 
    45use MLDBM qw(DB_File Storable); 
     6use DBI; 
    57use POSIX qw(strftime); 
     8use Getopt::Long; 
    69 
    710my %Files = ( 
    8     db_hash => '/etc/mail/spamassassin/FuzzyOcr.db', 
    9     db_safe => '/etc/mail/spamassassin/FuzzyOcr.safe.db', 
     11    db_hash => '/var/lib/fuzzyocr/FuzzyOcr.db', 
     12    db_safe => '/var/lib/fuzzyocr/FuzzyOcr.safe.db', 
    1013    ); 
    1114 
     15my %MySQL = ( 
     16     db   => 'FuzzyOcr' 
     17    ,hash => 'Hash' 
     18    ,safe => 'Safe' 
     19    ,user => 'fuzzyocr' 
     20    ,pass => 'fuzzyocr' 
     21    ,host => 'localhost' 
     22    ,port => 3306 
     23); 
     24 
     25my $cfgfile = "/etc/mail/spamassassin/FuzzyOcr.cf"; 
     26my %App; 
     27my $verbose = 0; 
     28my %opts; 
     29GetOptions( \%opts, 
     30        'all', 
     31        'colon|colons', 
     32        'config=s'      => \$cfgfile, 
     33        'dbtype=i', 
     34        'debug', 
     35        'help', 
     36        'verbose'       => \$verbose, 
     37); 
     38 
     39if ( $opts{'help'} ) { usage(); }; 
     40if ( $opts{'debug'} ) { use Data::Dumper; } 
     41 
    1242my %Stats = (); 
    1343 
    14 #Days 
    15 my $diff = shift @ARGV || 0; 
    16 my $gctr = shift @ARGV || 5; 
    17 my $time = time; 
    18 my $days = int($time/86400) - $diff; 
     44sub usage { 
     45    print "\n"; 
     46    print "fuzzy-stats [ options ] [ num days ago ] [ num entries to show ]\n"; 
     47    print "                            default 0          default 5\n"; 
     48    print "Options:    --all        Show all entries (ignore num days limit)\n"; 
     49    print "            --colons     Print hashes in colon delimited format\n"; 
     50    print "            --dbtype=2|3 Force MLDBM (2) or MySQL (3) data source\n"; 
     51    print "            --debug      Print ugly raw data\n"; 
     52    print "            --help       This screen\n"; 
     53    print "            --verbose    Print informational details\n"; 
     54    print "\n"; 
     55    exit; 
     56
    1957 
    20 foreach my $f (keys %Files) { 
    21     my %DB; my $err = 0; 
    22     tie %DB, 'MLDBM', $Files{$f} or ++$err; 
    23     next if $err; my @Top = (); 
    24     foreach my $k (keys %DB) { 
    25         my $db = $DB{$k}; 
    26         $Stats{$f}{'images'}++; 
    27         $Stats{$f}{'score'} += $db->{score}; 
    28         if (int($db->{check}/86400) == $days) { 
    29             $Stats{$f}{'today'}++; 
    30             $Stats{$f}{'score2'} += $db->{score}; 
    31             my @basic = split(':',$db->{basic}); 
    32             my $line; 
    33             if ($db->{score}) { 
    34                 $line = sprintf "%5d Time(s) -> %8.3f %9d %dx%dx%d" 
    35                     ,$db->{match}+1,$db->{score},@basic; 
    36             } else { 
    37                 $line = sprintf "%5d Time(s) -> %9d %dx%dx%d" 
    38                     ,$db->{match}+1,@basic; 
    39             } 
    40             foreach my $t (qw/fname ctype/) { 
    41                 $line .= sprintf ' %s',$db->{$t} if defined $db->{$t}; 
    42             } 
    43             push @Top,$line; 
    44         } 
    45         $Stats{$f}{'oldest'} = $db->{input} 
    46             unless $Stats{$f}{'oldest'}; 
    47         $Stats{$f}{'oldest'} = $db->{input} 
    48             if ($Stats{$f}{'oldest'} > $db->{input}); 
     58open CONFIG, "< $cfgfile" or warn "Can't read configuration file, using defaults...\n"; 
     59 
     60while (<CONFIG>) { 
     61    chomp; 
     62    if ($_ =~ m/^focr_enable_image_hashing (\d)/) { 
     63        $App{hashing_type} = $1; 
     64        printf "Found DB Hashing\n" if ($verbose and $1 == 2); 
     65        printf "Found MySQL Hashing\n" if ($verbose and $1 == 3); 
     66    } 
     67    if ($_ =~ m/^focr_mysql_(\w+) (.+)/) { 
     68        $MySQL{$1} = $2; 
     69        printf "Found MySQL option $1 => '$2'\n" if $verbose; 
    4970    } 
     71} 
     72 
     73close CONFIG; 
     74 
     75# Allow user to override location of data (2 -> MLDBM, 3 -> MySQL) 
     76$App{'hashing_type'} = $opts{'dbtype'} || $App{'hashing_type'}; 
     77 
     78sub get_ddb { 
     79    my %dopts = ( AutoCommit => 1 ); 
     80    my $dsn = "dbi:mysql:database=".$MySQL{db}; 
     81    if (defined $MySQL{socket}) { 
     82        $dsn .= ";mysql_socket=$MySQL{socket}"; 
     83    } else { 
     84        $dsn .= ";host=$MySQL{host}"; 
     85        $dsn .= ";port=$MySQL{port}" unless $MySQL{port} == 3306; 
     86    } 
     87    printf "Connecting to: $dsn\n" if $verbose; 
     88    return DBI->connect($dsn,$MySQL{user},$MySQL{pass},\%dopts); 
     89} 
     90 
     91sub print_summary { 
     92    my ($f,$time,$gctr,@Top) = @_; 
     93    # %Stats is a global variable 
    5094    my $s = $Stats{$f}; 
    5195    next unless $$s{'images'}; 
    5296 
     
    5498    my $p2 = sprintf "%12.2f",$$s{'score'}/$$s{'images'} if ($$s{'images'}); 
    5599    my $p3 = sprintf "%12.2f",$$s{'score2'}/$$s{'today'} if ($$s{'today'}); 
    56100 
    57     my @stat = stat($Files{$f}); 
    58101    printf "\n<<%s>>\n",$f; 
    59     printf "File Size    : %9d Bytes\n",$stat[7]; 
    60     printf "File Name    : %s\n",$Files{$f}; 
     102    if ( $App{'hashing_type'} == 2 ) { 
     103        # %Files is a global variable 
     104        my @stat = stat($Files{$f}); 
     105        printf "File Size    : %9d Bytes\n",$stat[7]; 
     106        printf "File Name    : %s\n",$Files{$f}; 
     107    } 
    61108    printf "Oldest Hash  : %s\n",scalar(localtime($$s{'oldest'})); 
    62109    printf "Average Score: %s\n",$p2 if ($$s{'score'}); 
    63110    printf "Images in DB : %9d\n",$$s{'images'}; 
     
    70117        last if (--$count == 0); 
    71118    } 
    72119} 
     120 
     121sub process_items { 
     122    my ($Stats,$f,$db,$days,%opts) = @_; 
     123    my @Text = (); 
     124 
     125    $Stats->{$f}{'images'}++; 
     126    $Stats->{$f}{'score'} += $db->{score}; 
     127    if (int($db->{check}/86400) == $days || $opts{'all'}) { 
     128        $Stats->{$f}{'today'}++; 
     129        $Stats->{$f}{'score2'} += $db->{score}; 
     130        my @basic = split(':',$db->{basic}); 
     131        my $line; 
     132        # Output is human readable by default, but if user wants to 
     133        # easily delete items, print in ::: format for easy cut/paste 
     134        my $format = $opts{'colon'} ? "%9d:%d:%d:%d" : "%9d %dx%dx%d"; 
     135        if ($db->{score}) { 
     136            $line = sprintf "%5d Time(s) -> %8.3f $format" 
     137                ,$db->{match}+1,$db->{score},@basic; 
     138        } else { 
     139            $line = sprintf "%5d Time(s) -> $format" 
     140                ,$db->{match}+1,@basic; 
     141        } 
     142        foreach my $t (qw/fname ctype/) { 
     143            $line .= sprintf ' %s',$db->{$t} if defined $db->{$t}; 
     144        } 
     145        push @Text, $line; 
     146    } 
     147    $Stats->{$f}{'oldest'} = $db->{input} 
     148        unless $Stats->{$f}{'oldest'}; 
     149    $Stats->{$f}{'oldest'} = $db->{input} 
     150        if ($Stats->{$f}{'oldest'} > $db->{input}); 
     151    #print Dumper @Text if $opts{debug}; 
     152    return @Text; 
     153} 
     154 
     155#Days 
     156my $diff = shift @ARGV || 0; 
     157my $gctr = shift @ARGV || 5; 
     158my $time = time; 
     159my $days = int($time/86400) - $diff; 
     160 
     161if ($App{hashing_type} == 2) { 
     162    foreach my $f (keys %Files) { 
     163        my %DB; my $err = 0; 
     164        tie %DB, 'MLDBM', $Files{$f} or ++$err; 
     165        next if $err; my @Top = (); 
     166        foreach my $k (keys %DB) { 
     167            my $db = $DB{$k}; 
     168            # Pass global %Stats reference and modify values in func 
     169            push @Top, &process_items(\%Stats,$f,$db,$days,%opts); 
     170        } 
     171        print_summary($f,$time,$gctr,@Top); 
     172    } 
     173} elsif ($App{hashing_type} == 3) { 
     174    my $key = '%'; 
     175    my $dbb = get_ddb(); 
     176    if ($dbb) { 
     177        my $now = time; 
     178        foreach my $f (keys %Files) { 
     179            my @Top = (); 
     180            my $db; 
     181            $f =~ s/db_//; 
     182            my $sql = "SELECT * FROM $MySQL{$f}"; 
     183            my $data = $dbb->selectall_arrayref($sql,undef); 
     184            foreach my $dbref (@{$data}) { 
     185                # Shove into the generic hashref to pass to process func  
     186                $db = { 'basic' => $dbref->[1], 
     187                        'fname' => $dbref->[2], 
     188                        'ctype' => $dbref->[3], 
     189                        'match' => $dbref->[5], 
     190                        'input' => $dbref->[6], 
     191                        'check' => $dbref->[7], 
     192                        'score' => $dbref->[8], 
     193                }; 
     194                # Pass global %Stats reference and modify values in func 
     195                push @Top, &process_items(\%Stats,$f,$db,$days,%opts); 
     196            } 
     197            print Dumper @Top if $opts{debug}; 
     198            print_summary($f,$time,$gctr,@Top); 
     199        } 
     200    } 
     201}