Basic support for returning MFHD for display purposes
authordbs <dbs@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sun, 26 Apr 2009 01:31:26 +0000 (01:31 +0000)
committerdbs <dbs@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sun, 26 Apr 2009 01:31:26 +0000 (01:31 +0000)
Interim measure, while there is a direct linkage between sre and bre,
is to invoke the bib_to_mfhd_hash method at record display time;
probably should return a structure including a count of parsed MFHD
records to shortcut cycling through all the arrays, but baby steps

git-svn-id: svn://svn.open-ils.org/ILS/trunk@12987 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/OpenILS/Application/Search.pm
Open-ILS/src/perlmods/OpenILS/Application/Search/Serial.pm [new file with mode: 0644]
Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm

index 36a57a0..4a5d878 100644 (file)
@@ -2574,6 +2574,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
             </actions>
         </permacrud>
        </class>
+       <class id="svr" controller="open-ils.cstore" oils_obj:fieldmapper="serial::virtual_record" oils_persist:virtual="true" reporter:label="Serial Virtual Record">
+               <fields>
+                       <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+                       <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+                       <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+                       <field name="holdings" oils_obj:array_position="3" oils_persist:virtual="true" />
+                       <field name="current_holdings" oils_obj:array_position="4" oils_persist:virtual="true" />
+                       <field name="supplements" oils_obj:array_position="5" oils_persist:virtual="true" />
+                       <field name="current_supplements" oils_obj:array_position="6" oils_persist:virtual="true" />
+                       <field name="indexes" oils_obj:array_position="7" oils_persist:virtual="true" />
+                       <field name="current_indexes" oils_obj:array_position="8" oils_persist:virtual="true" />
+                       <field name="online" oils_obj:array_position="9" oils_persist:virtual="true" />
+                       <field name="missing" oils_obj:array_position="10" oils_persist:virtual="true" />
+                       <field name="incomplete" oils_obj:array_position="11" oils_persist:virtual="true" />
+               </fields>
+       </class>
        <class id="sre" controller="open-ils.cstore" oils_obj:fieldmapper="serial::record_entry" oils_persist:tablename="serial.record_entry" reporter:label="Record Entry">
                <fields oils_persist:primary="id" oils_persist:sequence="serial.record_entry_id_seq">
                        <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
index fcce7e4..c70deb2 100644 (file)
@@ -15,6 +15,7 @@ use OpenILS::Application::Search::Authority;
 use OpenILS::Application::Search::Z3950;
 use OpenILS::Application::Search::Zips;
 use OpenILS::Application::Search::CNBrowse;
+use OpenILS::Application::Search::Serial;
 
 
 use OpenILS::Application::AppUtils;
diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Search/Serial.pm b/Open-ILS/src/perlmods/OpenILS/Application/Search/Serial.pm
new file mode 100644 (file)
index 0000000..008f7ff
--- /dev/null
@@ -0,0 +1,109 @@
+package OpenILS::Application::Search::Serial;
+use base qw/OpenILS::Application/;
+use strict; use warnings;
+
+
+use OpenSRF::Utils::JSON;
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::MFHDParser;
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenSRF::Utils::Cache;
+use Encode;
+
+use OpenSRF::Utils::Logger qw/:logger/;
+
+use Data::Dumper;
+
+use OpenSRF::Utils::JSON;
+
+use Time::HiRes qw(time);
+use OpenSRF::EX qw(:try);
+use Digest::MD5 qw(md5_hex);
+
+use XML::LibXML;
+use XML::LibXSLT;
+
+use OpenILS::Const qw/:const/;
+
+use OpenILS::Application::AppUtils;
+my $apputils = "OpenILS::Application::AppUtils";
+my $U = $apputils;
+
+my $pfx = "open-ils.search_";
+
+=over
+
+=item * mfhd_to_hash
+
+=back
+
+Takes an MFHD record ID and returns a hash of holdings statements
+
+=cut
+
+sub mfhd_to_hash {
+       my ($self, $client, $id) = @_;
+       
+       my $session = OpenSRF::AppSession->create("open-ils.cstore");
+       my $request = $session->request(
+                       "open-ils.cstore.direct.serial.record_entry.retrieve", $id )->gather(1);
+
+       my $u = OpenILS::Utils::MFHDParser->new();
+       my $mfhd_hash = $u->generate_svr( $request->marc );
+
+       $session->disconnect();
+       return $mfhd_hash;
+}
+
+__PACKAGE__->register_method(
+       method  => "mfhd_to_hash",
+       api_name        => "open-ils.search.serial.record.mfhd.retrieve",
+       argc            => 1, 
+       note            => "Given a serial record ID, return MFHD holdings"
+);
+
+=over
+
+=item * bib_to_mfhd_hash 
+
+=back
+
+Given a bib record ID, returns a hash of holdings statements
+
+=cut
+
+sub bib_to_mfhd_hash {
+       my ($self, $client, $bib) = @_;
+       
+       my $mfhd_hash;
+
+       # miker suggested this way, but I'm too stupid to get it to work
+#      my $e = OpenILS::Utils::CStoreEditor->new();
+#      my $mfhd = $e->search_serial_record_entry({bib=>$bib});
+
+       my @mfhd = $U->cstorereq( "open-ils.cstore.json_query.atomic", {
+               select  => { sre => 'marc' },
+               from    => 'sre',
+               where   => { record => $bib },
+               distinct => 1
+       });
+       
+       if (!@mfhd or scalar(@mfhd) == 0) {
+               return undef;
+       }
+
+       my $u = OpenILS::Utils::MFHDParser->new();
+       $mfhd_hash = $u->generate_svr( $mfhd[0][0]->{marc} );
+
+       return $mfhd_hash;
+}
+
+__PACKAGE__->register_method(
+       method  => "bib_to_mfhd_hash",
+       api_name        => "open-ils.search.serial.record.bib_to_mfhd.retrieve",
+       argc            => 1, 
+       note            => "Given a bibliographic record ID, return MFHD holdings"
+);
+
+1;
index 5995de9..36e8bcf 100644 (file)
-#!/usr/bin/perl -w
-use strict;
-use Date::Manip;
-
-# Parse MFHD patterns for http://www.loc.gov/marc/holdings/hd853855.html
-
-# Primary goal:
-# Expected input: a chunk of MFHD, a start date, and # of issuances to project
-# Expected output: a set of issuances projected forward from the start date,
-#   with issue/volume/dates/captions conforming to what the MFHD actually says
-
-# The thought had been to use Date::Manip to generate the actual dates for
-# each issuance, like:
-#
-#   # To find the 2nd Tuesday of every month
-#   @date = ParseRecur("0:1*2:2:0:0:0",$base,$start,$stop);
-
-# Secondary goal: generate automatic summary holdings
-#   (a la http://www.loc.gov/marc/holdings/hd863865.html)
-
-# Compressability comes from first indicator
-sub parse_compressability {
-       my $c = shift || return undef;
-
-       my %compressability = (
-               '0' => 'Cannot compress or expand',
-               '1' => 'Can compress but cannot expand',
-               '2' => 'Can compress or expand',
-               '3' => 'Unknown',
-               '#' => 'Undefined'
-       );
-
-       if (exists $compressability{$c}) {
-               return $compressability{$c};
+package OpenILS::Utils::MFHDParser;
+use strict; use warnings;
+
+use OpenSRF::EX qw/:try/;
+use Time::HiRes qw(time);
+use OpenILS::Utils::Fieldmapper;
+use OpenSRF::Utils::SettingsClient;
+use OpenSRF::Utils::Logger qw/$logger/;
+
+use OpenILS::Utils::MFHD;
+use MARC::File::XML;
+use Data::Dumper;
+
+sub new { return bless( {}, shift() ); }
+
+=head1 Subroutines
+
+=over
+
+=item * format_textual_holdings($field)
+
+=back
+
+Returns concatenated subfields $a with $z for textual holdings (866-868)
+
+=cut
+
+sub format_textual_holdings {
+       my ($self, $field) = @_;
+       my $holdings;
+       my $public_note;
+
+       $holdings = $field->subfield('a');
+       if (!$holdings) {
+               return undef;
+       }
+
+       $public_note = $field->subfield('z');
+       if ($public_note) {
+               return "$holdings - $public_note";
        }
-       # 'Unknown compressability indicator - expected one of (0,1,2,3,#)';
-       return undef;
+       return $holdings;
 }
 
-# Caption evaluation comes from second indicator
-sub caption_evaluation {
-       my $ce = shift || return undef;
+=over
 
-       my %caption_evaluation = (
-               '0' => 'Captions verified; all levels present',
-               '1' => 'Captions verified; all levels may not be present',
-               '2' => 'Captions unverified; all levels present',
-               '3' => 'Captions unverified; all levels may not be present',
-               '#' => 'Undefined',
-       );
+=item * mfhd_to_hash($mfhd_xml)
 
-       if (exists $caption_evaluation{$ce}) {
-               return $caption_evaluation{$ce};
+=back
+
+Returns a Perl hash containing fields of interest from the MFHD record
+
+=cut
+sub mfhd_to_hash {
+       my ($self, $mfhd_xml) = @_;
+
+       my $holdings = [];
+       my $supplements = [];
+       my $indexes = [];
+       my $current_holdings = [];
+       my $current_supplements = [];
+       my $current_indexes = [];
+       my $online = []; # Laurentian extension to MFHD standard
+       my $missing = []; # Laurentian extension to MFHD standard
+       my $incomplete = []; # Laurentian extension to MFHD standard
+
+       my $marc = MARC::Record->new_from_xml($mfhd_xml);
+       my $mfhd = OpenILS::Utils::MFHD->new($marc);
+
+       foreach my $field ($marc->field('866')) {
+               my $textual_holdings = $self->format_textual_holdings($field);
+               if ($textual_holdings) {
+                       push @$holdings, $textual_holdings;
+               }
+       }
+       foreach my $field ($marc->field('867')) {
+               my $textual_holdings = $self->format_textual_holdings($field);
+               if ($textual_holdings) {
+                       push @$supplements, $textual_holdings;
+               }
+       }
+       foreach my $field ($marc->field('868')) {
+               my $textual_holdings = $self->format_textual_holdings($field);
+               if ($textual_holdings) {
+                       push @$indexes, $textual_holdings;
+               }
        }
-       # 'Unknown caption evaluation indicator - expected one of (0,1,2,3,#)';
-       return undef;
-}
 
-# Start with frequency ($w)
-# then overlay number of pieces of issuance ($p)
-# then regularity pattern ($y)
-my %frequency = (
-       'a' => 'annual',
-       'b' => 'bimonthly',
-       'c' => 'semiweekly',
-       'd' => 'daily',
-       'e' => 'biweekly',
-       'f' => 'semiannual',
-       'g' => 'biennial',
-       'h' => 'triennial',
-       'i' => 'three times a week',
-       'j' => 'three times a month',
-       'k' => 'continuously updated',
-       'm' => 'monthly',
-       'q' => 'quarterly',
-       's' => 'semimonthly',
-       't' => 'three times a year',
-       'w' => 'weekly',
-       'x' => 'completely irregular',
-);
-
-sub parse_frequency {
-       my $freq = shift || return undef;
-
-       if ($freq =~ m/^\d+$/) {
-               return "$freq times a year";
-       } elsif (exists $frequency{$freq}) {
-               return $frequency{$freq};
+       foreach my $cap_id ($mfhd->captions('853')) {
+               my @curr_holdings = $mfhd->holdings('863', $cap_id);
+               next unless scalar @curr_holdings;
+               foreach (@curr_holdings) {
+                       push @$current_holdings, $_->format();
+               }
        }
-       # unrecognized frequency specification
-       return undef;
-}
 
-# $x - Point at which the highest level increments or changes
-# Interpretation of two-digit numbers in the 01-12 range depends on the publishing frequency
-# More than one change can be passed in the subfield and are delimited by commas
-sub chronology_change {
-       my $chronology_change = shift || return undef;
-       my @c_changes = split /,/, $chronology_change;
-       foreach my $change (@c_changes) {
-               if ($change == 21) {
-                       
+       foreach my $cap_id ($mfhd->captions('854')) {
+               my @curr_supplements = $mfhd->holdings('864', $cap_id);
+               next unless scalar @curr_supplements;
+               foreach (@curr_supplements) {
+                       push @$current_supplements, $_->format();
                }
        }
-       return undef;
-}
 
-# Publication code : first character in regularity pattern ($y)
-sub parse_publication_code {
-       my $c = shift || return undef;
+       foreach my $cap_id ($mfhd->captions('855')) {
+               my @curr_indexes = $mfhd->holdings('865', $cap_id);
+               next unless scalar @curr_indexes;
+               foreach (@curr_indexes) {
+                       push @$current_indexes, $_->format();
+               }
+       }
 
-       my %publication_code = (
-               'c' => 'combined',
-               'o' => 'omitted',
-               'p' => 'published',
-               '#' => 'undefined',
-       );
+       # Laurentian extensions
+       foreach my $field ($marc->field('530')) {
+               my $online_stmt = $self->format_textual_holdings($field);
+               if ($online_stmt) {
+                       push @$online, $online_stmt;
+               }
+       }
 
-       if (exists $publication_code{$c}) {
-               return $publication_code{$c};
+       foreach my $field ($marc->field('590')) {
+               my $missing_stmt = $self->format_textual_holdings($field);
+               if ($missing_stmt) {
+                       push @$missing, $missing_stmt;
+               }
        }
-       return undef;
-}
 
-# Chronology code : part of regularity pattern ($y)
-sub parse_chronology_code {
-       my $c = shift || return undef;
-
-       my %chronology_code = (
-               'd' => 'Day',
-               'm' => 'Month',
-               's' => 'Season',
-               'w' => 'Week',
-               'y' => 'Year',
-               'e' => 'Enumeration',
-       );
-
-       if (exists $chronology_code{$c}) {
-               return $chronology_code{$c};
+       foreach my $field ($marc->field('591')) {
+               my $incomplete_stmt = $self->format_textual_holdings($field);
+               if ($incomplete_stmt) {
+                       push @$incomplete, $incomplete_stmt;
+               }
        }
-       return undef;
-}
 
-sub parse_regularity_pattern {
-       my $pattern = shift;
-       my ($pc, $cc, $cd) = $pattern =~ m{^(\w)(\w)(.+)$};
-       
-       my $pub_code = parse_publication_code($pc);
-       my $chron_code = parse_chronology_code($cc);
-       my $chron_def = parse_chronology_definition($cd);
-       
-       return ($pub_code, $chron_code, $chron_def);
+       return { holdings => $holdings, current_holdings => $current_holdings,
+                       supplements => $supplements, current_supplements => $current_supplements,
+                       indexes => $indexes, current_indexes => $current_indexes,
+                       missing => $missing, incomplete => $incomplete, };
 }
 
-sub parse_chronology_definition {
-       my $chron_def = shift || return undef;
-       # Well, this is where it starts to get hard, doesn't it?
-       return $chron_def;
+=over
+
+=item * init_holdings_virtual_record()
+
+=back
+
+Initialize the serial virtual record (svr) instance
+
+=cut
+sub init_holdings_virtual_record {
+       my $record = Fieldmapper::serial::virtual_record->new;
+       $record->holdings([]);
+       $record->current_holdings([]);
+       $record->supplements([]);
+       $record->current_supplements([]);
+       $record->indexes([]);
+       $record->current_indexes([]);
+       $record->online([]);
+       $record->missing([]);
+       $record->incomplete([]);
+       return $record;
 }
 
-print parse_regularity_pattern("cddd");
-print "\n";
-print parse_regularity_pattern("38dd");
-print "\n";
+=over
 
-1;
+=item * init_holdings_virtual_record($mfhd)
+
+=back
+
+Given an MFHD record, return a populated svr instance
+
+=cut
+sub generate_svr {
+       my ($self, $mfhd) = @_;
+
+       if (!$mfhd) {
+               return undef;
+       }
+
+       my $record = init_holdings_virtual_record();
+       my $holdings = $self->mfhd_to_hash($mfhd);
+
+       $record->holdings($holdings->{holdings});
+       $record->current_holdings($holdings->{current_holdings});
+       $record->supplements($holdings->{supplements});
+       $record->current_supplements($holdings->{current_supplements});
+       $record->indexes($holdings->{indexes});
+       $record->current_indexes($holdings->{current_indexes});
+       $record->online($holdings->{online});
+       $record->missing($holdings->{missing});
+       $record->incomplete($holdings->{incomplete});
 
-# :vim:noet:ts=4:sw=4:
+       return $record;
+}
+
+1;