r1042@llin: dpavlin | 2006-09-29 20:53:01 +0200
[webpac2] / lib / WebPAC / Normalize.pm
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