r1042@llin: dpavlin | 2006-09-29 20:53:01 +0200
authorDobrica Pavlinusic <dpavlin@rot13.org>
Fri, 29 Sep 2006 18:55:41 +0000 (18:55 +0000)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Fri, 29 Sep 2006 18:55:41 +0000 (18:55 +0000)
 added _set_load_ds, and load_ds_coderef to data_structure so that lookups can call
 closure to fetch records, implemented lookup based on new format generated by
 WebPAC::Parser, test to be sure that it somewhat works :-) [0.21]

git-svn-id: svn+ssh://mjesec/home/dpavlin/svn/webpac2/trunk@725 07558da8-63fa-0310-ba24-9fe276d99e06

lib/WebPAC/Normalize.pm
t/3-normalize.t

index 13c76e6..9c37614 100644 (file)
@@ -2,6 +2,7 @@ package WebPAC::Normalize;
 use Exporter 'import';
 @EXPORT = qw/
        _set_rec _set_lookup
+       _set_load_ds
        _get_ds _clean_ds
        _debug
        _pack_subfields_hash
@@ -26,6 +27,7 @@ use strict;
 #use base qw/WebPAC::Common/;
 use Data::Dump qw/dump/;
 use Storable qw/dclone/;
+use Carp qw/confess/;
 
 # debugging warn(s)
 my $debug = 0;
@@ -37,11 +39,11 @@ WebPAC::Normalize - describe normalisaton rules using sets
 
 =head1 VERSION
 
-Version 0.20
+Version 0.21
 
 =cut
 
-our $VERSION = '0.20';
+our $VERSION = '0.21';
 
 =head1 SYNOPSIS
 
@@ -67,16 +69,23 @@ All other functions are available for use within normalisation rules.
 Return data structure
 
   my $ds = WebPAC::Normalize::data_structure(
-       lookup => $lookup_variable,
+       lookup => $lookup_hash,
        row => $row,
        rules => $normalize_pl_config,
        marc_encoding => 'utf-8',
        config => $config,
+       load_ds_coderef => sub {
+               my ($database,$input,$mfn) = shift;
+               $store->load_ds( database => $database, input => $input, id => $mfn );
+       },
   );
 
 Options C<row>, C<rules> and C<log> are mandatory while all
 other are optional.
 
+C<load_ds_coderef> is closure only used when executing lookups, so they will
+die if it's not defined.
+
 This function will B<die> if normalizastion can't be evaled.
 
 Since this function isn't exported you have to call it with 
@@ -84,6 +93,8 @@ C<WebPAC::Normalize::data_structure>.
 
 =cut
 
+my $load_ds_coderef;
+
 sub data_structure {
        my $arg = {@_};
 
@@ -91,10 +102,12 @@ sub data_structure {
        die "need normalisation argument" unless ($arg->{rules});
 
        no strict 'subs';
-       _set_lookup( $arg->{lookup} ) if (defined( $arg->{lookup} ));
+       _set_lookup( $arg->{lookup} );
        _set_rec( $arg->{row} );
-       _set_config( $arg->{config} ) if (defined( $arg->{config} ));
+       _set_config( $arg->{config} );
        _clean_ds( %{ $arg } );
+       $load_ds_coderef = $arg->{load_ds_coderef};
+
        eval "$arg->{rules}";
        die "error evaling $arg->{rules}: $@\n" if ($@);
 
@@ -199,6 +212,25 @@ sub _get_lookup {
        return $lookup;
 }
 
+=head2 _set_load_ds
+
+Setup code reference which will return L<data_structure> from
+L<WebPAC::Store>
+
+  _set_load_ds(sub {
+               my ($database,$input,$mfn) = @_;
+               $store->load_ds( database => $database, input => $input, id => $mfn );
+  });
+
+=cut
+
+sub _set_load_ds {
+       my $coderef = shift;
+       confess "argument isn't CODE" unless ref($coderef) eq 'CODE';
+
+       $load_ds_coderef = $coderef;
+}
+
 =head2 _get_marc_fields
 
 Get all fields defined by calls to C<marc>
@@ -941,32 +973,120 @@ sub first {
 
 Consult lookup hashes for some value
 
-  @v = lookup( $v );
-  @v = lookup( @v );
+  @v = lookup(
+       sub {
+               'ffkk/peri/mfn'.rec('000')
+       },
+       'ffkk','peri','200-a-200-e',
+       sub {
+               first(rec(200,'a')).' '.first(rec('200','e'))
+       }
+  );
 
-FIXME B<currently this one is broken!>
+Code like above will be B<automatically generated> using L<WebPAC::Parse> from
+normal lookup definition in C<conf/lookup/something.pl> which looks like:
+
+  lookup(
+       # which results to return from record recorded in lookup
+       sub { 'ffkk/peri/mfn' . rec('000') },
+       # from which database and input
+       'ffkk','peri',
+       # such that following values match
+       sub { first(rec(200,'a')) . ' ' . first(rec('200','e')) },
+       # if this part is missing, we will try to match same fields
+       # from lookup record and current one, or you can override
+       # which records to use from current record using
+       sub { rec('900','x') . ' ' . rec('900','y') },
+  )
+
+You can think about this lookup as SQL (if that helps):
+
+  select
+       sub { what }
+  from
+       database, input
+  where
+    sub { filter from lookuped record }
+  having
+    sub { optional filter on current record }
+
+Easy as pie, right?
 
 =cut
 
 sub lookup {
-       my $k = shift or return;
-       return unless (defined($lookup->{$k}));
-       if (ref($lookup->{$k}) eq 'ARRAY') {
-               return @{ $lookup->{$k} };
-       } else {
-               return $lookup->{$k};
+       my ($what, $database, $input, $key, $having) = @_;
+
+       confess "lookup needs 5 arguments: what, database, input, key, having" unless ($#_ == 4);
+
+       warn "# lookup ($database, $input, $key)\n";
+       return unless (defined($lookup->{$database}->{$input}->{$key}));
+
+       confess "lookup really need load_ds_coderef added to data_structure\n" unless ($load_ds_coderef);
+
+       my $mfns;
+       my @having = $having->();
+
+       warn "## having = ", dump( @having );
+
+       foreach my $h ( @having ) {
+               if (defined($lookup->{$database}->{$input}->{$key}->{$h})) {
+                       warn "lookup for $database/$input/$key/$h return ",dump($lookup->{$database}->{$input}->{$key}->{$h}),"\n";
+                       $mfns->{$_}++ foreach keys %{ $lookup->{$database}->{$input}->{$key}->{$h} };
+               }
        }
+
+       return unless ($mfns);
+
+       my @mfns = sort keys %$mfns;
+
+       warn "# lookup loading $database/$input/$key mfn ", join(",",@mfns)," having ",dump(@having),"\n";
+
+       my $old_rec = $rec;
+       my @out;
+
+       foreach my $mfn (@mfns) {
+               $rec = $load_ds_coderef->( $database, $input, $mfn );
+
+               warn "got $database/$input/$mfn = ", dump($rec), $/;
+
+               my @vals = $what->();
+
+               push @out, ( @vals );
+
+               warn "lookup for mfn $mfn returned ", dump(@vals), $/;
+       }
+
+#      if (ref($lookup->{$k}) eq 'ARRAY') {
+#              return @{ $lookup->{$k} };
+#      } else {
+#              return $lookup->{$k};
+#      }
+
+       $rec = $old_rec;
+
+       warn "## lookup returns = ", dump(@out), $/;
+
+       return @out;
 }
 
 =head2 save_into_lookup
 
-Save value into lookup.
+Save value into lookup. It associates current database, input
+and specific keys with one or more values which will be
+associated over MFN.
+
+MFN will be extracted from first occurence current of field 000
+in current record, or if it doesn't exist from L<_set_config> C<_mfn>.
 
-  save_into_lookup($database,$input,$key,sub {
+  my $nr = save_into_lookup($database,$input,$key,sub {
        # code which produce one or more values 
   });
 
-This function shouldn't be called directly, it's called from code created by L<WebPAC::Parser>.
+It returns number of items saved.
+
+This function shouldn't be called directly, it's called from code created by
+L<WebPAC::Parser>. 
 
 =cut
 
@@ -976,11 +1096,23 @@ sub save_into_lookup {
        die "save_into_lookup needs input" unless defined($input);
        die "save_into_lookup needs key" unless defined($key);
        die "save_into_lookup needs CODE" unless ( defined($coderef) && ref($coderef) eq 'CODE' );
-       my $mfn = $rec->{'000'}->[0] || die "mfn not defined or zero";
+
+warn "## save_into_lookup rec = ", dump($rec), " config = ", dump($config),"\n";
+
+       my $mfn = 
+               defined($rec->{'000'}->[0])     ?       $rec->{'000'}->[0]      :
+               defined($config->{_mfn})        ?       $config->{_mfn}         :
+                                                                               die "mfn not defined or zero";
+
+       my $nr = 0;
+
        foreach my $v ( $coderef->() ) {
                $lookup->{$database}->{$input}->{$key}->{$v}->{$mfn}++;
                warn "# saved lookup $database/$input/$key [$v] $mfn\n" if ($debug > 1);
+               $nr++;
        }
+
+       return $nr;
 }
 
 =head2 config
index e5ab7ab..8a727a7 100755 (executable)
@@ -2,7 +2,7 @@
 
 use strict;
 
-use Test::More tests => 157;
+use Test::More tests => 309;
 use Test::Exception;
 use Cwd qw/abs_path/;
 use blib;
@@ -32,8 +32,6 @@ ok(my $abs_path = abs_path($0), "abs_path");
 $abs_path =~ s#/[^/]*$#/#;
 diag "abs_path: $abs_path" if ($debug);
 
-#throws_ok { new WebPAC::Normalize::XML( lookup_regex => 'foo' ) } qr/pair/, "lookup_regex without lookup";
-
 my $rec1 = {
        '200' => [{
                'a' => '200a',
@@ -109,18 +107,34 @@ my $rec2 = {
 };
 
 
-my $lookup1 = {
-       '00900' => [
-               'lookup 1',
-               'lookup 2',
-       ],
+my $lookup_hash1 = {
+       'db1' => {
+               'input1' => {
+                       'key1' => { 1 => 1 },
+                       'key2' => { 2 => 1 },
+               },
+               'input2' => {
+                       'key3' => { 3 => 1 },
+                       'key4' => { 4 => 1 },
+               },
+       },
+       'db2' => {
+               'input3' => {
+                       'key5' => { 5 => 1 },
+                       'key6' => { 6 => 1 },
+               },
+       }
 };
 
-my $lookup2 = {
-       '00900' => 'lookup',
+my $lookup_hash2 = {
+       'db3' => {
+               'input4' => {
+                       'key7' => { 7 => 1 },
+                       'key8' => { 8 => 1 },
+               },
+       }
 };
 
-
 sub test {
        print dump( @_ ), ("-" x 78), "\n";
        ok( defined(@_) );
@@ -180,18 +194,118 @@ sub test_s {
        cmp_ok( join('', suffix('xy', 'cd') ), 'eq', 'cdxy', 'suffix');
        cmp_ok( join('', surround('->', '<-', 'a','b','c') ), 'eq', '->a<-->b<-->c<-', 'surround');
 
+       # lookups
+
+       throws_ok { _set_load_ds() } qr/CODE/, 'empty _set_load_ds()';
+
+       ok(_set_load_ds(sub {
+               my ($database,$input,$mfn) = @_;
+               diag "load_ds( $database, $input, $mfn )";
+               cmp_ok( $#_, '==', 2, 'have 3 arguments');
+               ok($database, '_load_ds database');
+               ok($input, '_load_ds input');
+               ok($mfn, '_load_ds mfn');
+               return {
+                       '900' => [{ x => '900x-' . $mfn , y => '900y-' . $mfn }],
+               }
+
+       }), '_set_load_ds');
+
+       my @v = qw/foo bar baz aaa bbb ccc ddd/;
+
+       my @accumulated;
+
+       for my $i ( 0 .. $#v ) {
+
+               my $mfn = 1000 + $i;
+
+               ok(WebPAC::Normalize::_set_config({ '_mfn' => $mfn }), "_set_config _mfn=$mfn");
+
+               my $size = $#v + 1;
+
+               cmp_ok(
+                       save_into_lookup('db','input','key', sub { @v }),
+                       '==', $size, "save_into_lookup $size values"
+               );
+
+               ok(my $l = WebPAC::Normalize::_get_lookup(), '_get_lookup');
+               diag "_get_lookup = ", dump($l);
+
+               my @lookup;
+
+               ok(my @lookup = lookup(
+                               sub {
+                                       diag "in show";
+                                       rec('900','x');
+                               },
+                               'db','input','key',
+                               sub {
+                                       return @v;
+                               }
+                       ),
+               "lookup db/input/key");
+
+               push @accumulated, '900x-' . $mfn;
+
+               is_deeply(\@lookup, \@accumulated, "lookup db/input/key");
+
+               shift @v;
+
+       }
+
+       ok(my $l = WebPAC::Normalize::_get_lookup(), '_get_lookup');
+       diag "_get_lookup = ", dump($l);
+
+       is_deeply( $l, {
+               db => {
+                       input => {
+                               key => {
+                                       foo => { 1000 => 1 },
+                                       bar => { 1000 => 1, 1001 => 1 },
+                                       baz => { 1000 => 1, 1001 => 1, 1002 => 1 },
+                                       aaa => { 1000 => 1, 1001 => 1, 1002 => 1, 1003 => 1 },
+                                       bbb => { 1000 => 1, 1001 => 1, 1002 => 1, 1003 => 1, 1004 => 1 },
+                                       ccc => { 1000 => 1, 1001 => 1, 1002 => 1, 1003 => 1, 1004 => 1, 1005 => 1 },
+                                       ddd => { 1000 => 1, 1001 => 1, 1002 => 1, 1003 => 1, 1004 => 1, 1005 => 1, 1006 => 1 },
+                               },
+                       },
+               },
+       }, 'lookup data');
+
+#######
+
+       diag "lookup_hash1 = ", dump($lookup_hash1);
+       ok(_set_lookup( $lookup_hash1 ), '_set_lookup $lookup_hash1');
+
+       throws_ok { _set_load_ds() } qr/CODE/, 'empty _set_load_ds()';
+
+       ok(_set_load_ds(sub {
+               my ($database,$input,$mfn) = @_;
+               diag "load_ds( $database, $input, $mfn )";
+               cmp_ok( $#_, '==', 2, 'have 3 arguments');
+               ok($database, 'database');
+               ok($input, 'input');
+               ok($mfn, 'mfn');
+
+       }), '_set_load_ds');
+
+
+#      cmp_ok(lookup(
+#              sub {
+#                      'found'
+#              },
+#              'db1','input1','key1',
+#              sub {
+#                      rec('200','a')
+#              }
+#      ), 'eq', 'found', 'lookup db1/input1/key1');
+
 
-       _set_lookup( $lookup1 );
        
-       cmp_ok(
-               join_with(" i ",
-                       lookup( 
-                               regex( 's/^/00/',
-                                       rec2('902','z')
-                               )
-                       ) 
-               ),
-       'eq', 'lookup 1 i lookup 2', 'join lookup regex rec2');
+#      cmp_ok(
+#              lookup( 
+#              ),
+#      'eq', 'lookup 1 i lookup 2', 'join lookup regex rec2');
 
        # check join_with operations
 
@@ -235,11 +349,13 @@ sub test_s {
 
        # test lookups
 
-       _set_lookup( $lookup2 );
+       _set_lookup( $lookup_hash2 );
+
+       throws_ok { lookup() } qr/need/, 'empty lookup';
 
-       is_deeply( \[ lookup( prefix( '00', rec('902') ) ) ], \[ 'lookup' ], 'lookup prefix' );
+       #is_deeply( \[ lookup( prefix( '00', rec('902') ) ) ], \[ 'lookup' ], 'lookup prefix' );
 
-       ok(! lookup('non-existent'), 'lookup non-existant' );
+       #ok(! lookup('non-existent'), 'lookup non-existant' );
 
        _set_rec( $rec2 );
 
@@ -360,7 +476,7 @@ sub test_s {
        #
        # MARC
        #
-       _debug( 4 );
+       #_debug( 4 );
 
        test_s(qq{ marc_indicators('900',1,2) });
        test_s(qq{ marc('900','a', rec('200') ) });