From 6542a17ea84e42e6c2cf51fab41b43212f85b2bc Mon Sep 17 00:00:00 2001 From: erickson Date: Fri, 19 Sep 2008 16:26:30 +0000 Subject: [PATCH 1/1] moved marc edit/import functionaly out to an external module so it can be imported by other modules (namely, vandelay). this will allow said modules to manipulate bib records inside of a single transaction. cleaned out some old, unused code git-svn-id: svn://svn.open-ils.org/ILS/trunk@10640 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- .../src/perlmods/OpenILS/Application/Cat.pm | 331 +----------------- .../OpenILS/Application/Cat/BibCommon.pm | 273 +++++++++++++++ 2 files changed, 284 insertions(+), 320 deletions(-) create mode 100644 Open-ILS/src/perlmods/OpenILS/Application/Cat/BibCommon.pm diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm b/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm index 03547b113a..1bba432bfa 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm @@ -4,6 +4,7 @@ use OpenILS::Application::AppUtils; use OpenILS::Application; use OpenILS::Application::Cat::Merge; use OpenILS::Application::Cat::Authority; +use OpenILS::Application::Cat::BibCommon; use base qw/OpenILS::Application/; use Time::HiRes qw(time); use OpenSRF::EX qw(:try); @@ -170,55 +171,17 @@ __PACKAGE__->register_method( sub biblio_record_replace_marc { my( $self, $conn, $auth, $recid, $newxml, $source ) = @_; - my $e = new_editor(authtoken=>$auth, xact=>1); return $e->die_event unless $e->checkauth; return $e->die_event unless $e->allowed('CREATE_MARC', $e->requestor->ws_ou); - my $rec = $e->retrieve_biblio_record_entry($recid) - or return $e->die_event; - - my $fixtcn = 1 if $self->api_name =~ /replace/o; - - # See if there is a different record in the database that has our TCN value - # If we're not updating the TCN, all we care about it the marcdoc - my $override = $self->api_name =~ /override/; - - # XXX should .update even bother with the tcn_info if it's not going to replace it? - # there is the potential for returning a TCN_EXISTS event, even though no replacement happens - - my( $tcn, $tsource, $marcdoc, $evt); - - if($fixtcn or $override) { - - ($tcn, $tsource, $marcdoc, $evt) = - _find_tcn_info($e, $newxml, $override, $recid); + my $res = OpenILS::Application::Cat::BibCommon->biblio_record_replace_marc( + $e, $recid, $newxml, $source, + $self->api_name =~ /replace/o, + $self->api_name =~ /override/o); - return $evt if $evt; - - $rec->tcn_value($tcn) if ($tcn); - $rec->tcn_source($tsource); - - } else { - - $marcdoc = __make_marc_doc($newxml); - } - - - $rec->source(bib_source_from_name($source)) if $source; - $rec->editor($e->requestor->id); - $rec->edit_date('now'); - $rec->marc( $U->entityize( $marcdoc->documentElement->toString ) ); - $e->update_biblio_record_entry($rec) or return $e->die_event; - $e->commit; - - $conn->respond_complete($rec); - - $U->simplereq( - 'open-ils.ingest', - 'open-ils.ingest.full.biblio.record', $recid ); - - return undef; + $e->commit unless $U->event_code($res); + return $res; } __PACKAGE__->register_method( @@ -297,230 +260,17 @@ __PACKAGE__->register_method( sub biblio_record_xml_import { my( $self, $client, $authtoken, $xml, $source, $auto_tcn) = @_; - - my $override = 1 if $self->api_name =~ /override/; my $e = new_editor(xact=>1, authtoken=>$authtoken); return $e->die_event unless $e->checkauth; return $e->die_event unless $e->allowed('IMPORT_MARC', $e->requestor->ws_ou); - my( $evt, $tcn, $tcn_source, $marcdoc ); - - if( $auto_tcn ) { - # auto_tcn forces a blank TCN value so the DB will have to generate one for us - $marcdoc = __make_marc_doc($xml); - } else { - ( $tcn, $tcn_source, $marcdoc, $evt ) = _find_tcn_info($e, $xml, $override); - return $evt if $evt; - } - - $logger->info("user ".$e->requestor->id. - " creating new biblio entry with tcn=$tcn and tcn_source $tcn_source"); - - my $record = Fieldmapper::biblio::record_entry->new; - - $record->source(bib_source_from_name($source)) if $source; - $record->tcn_source($tcn_source); - $record->tcn_value($tcn) if ($tcn); - $record->creator($e->requestor->id); - $record->editor($e->requestor->id); - $record->create_date('now'); - $record->edit_date('now'); - $record->marc( $U->entityize( $marcdoc->documentElement->toString ) ); - - $record = $e->create_biblio_record_entry($record) or return $e->die_event; - $logger->info("marc create/import created new record ".$record->id); - - $e->commit; - - $logger->debug("Sending record off to be ingested and indexed"); - - $client->respond_complete($record); - - $U->simplereq( - 'open-ils.ingest', - 'open-ils.ingest.full.biblio.record', $record->id ); - - return undef; -} - -sub __make_marc_doc { - my $xml = shift; - my $marcxml = XML::LibXML->new->parse_string( $xml ); - $marcxml->documentElement->setNamespace( - "http://www.loc.gov/MARC21/slim", "marc", 1 ); - $marcxml->documentElement->setNamespace("http://www.loc.gov/MARC21/slim"); - return $marcxml; -} - - -sub _find_tcn_info { - my $editor = shift; - my $xml = shift; - my $override = shift; - my $existing_rec = shift || 0; - - # parse the XML - my $marcxml = __make_marc_doc($xml); - - my $xpath = '//marc:controlfield[@tag="001"]'; - my $tcn = $marcxml->documentElement->findvalue($xpath); - $logger->info("biblio import located 001 (tcn) value of $tcn"); - - $xpath = '//marc:controlfield[@tag="003"]'; - my $tcn_source = $marcxml->documentElement->findvalue($xpath) || "System Local"; - - if(my $rec = _tcn_exists($editor, $tcn, $tcn_source, $existing_rec) ) { - - my $origtcn = $tcn; - $tcn = find_free_tcn( $marcxml, $editor, $existing_rec ); - - # if we're overriding, try to find a different TCN to use - if( $override ) { - - # XXX Create ALLOW_ALT_TCN permission check support - - $logger->info("tcn value $tcn already exists, attempting to override"); - - if(!$tcn) { - return ( - undef, - undef, - undef, - OpenILS::Event->new( - 'OPEN_TCN_NOT_FOUND', - payload => $marcxml->toString()) - ); - } - - } else { - - $logger->warn("tcn value $origtcn already exists in import/create"); - - # otherwise, return event - return ( - undef, - undef, - undef, - OpenILS::Event->new( - 'TCN_EXISTS', payload => { - dup_record => $rec, - tcn => $origtcn, - new_tcn => $tcn - } - ) - ); - } - } - - return ($tcn, $tcn_source, $marcxml); -} - -sub find_free_tcn { - - my $marcxml = shift; - my $editor = shift; - my $existing_rec = shift; - - my $add_039 = 0; - - my $xpath = '//marc:datafield[@tag="039"]/subfield[@code="a"]'; - my ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; - $xpath = '//marc:datafield[@tag="039"]/subfield[@code="b"]'; - my $tcn_source = $marcxml->documentElement->findvalue($xpath) || "System Local"; - - if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) { - $tcn = undef; - } else { - $add_039++; - } - - - if(!$tcn) { - $xpath = '//marc:datafield[@tag="020"]/subfield[@code="a"]'; - ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; - $tcn_source = "ISBN"; - if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} - } - - if(!$tcn) { - $xpath = '//marc:datafield[@tag="022"]/subfield[@code="a"]'; - ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; - $tcn_source = "ISSN"; - if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} - } - - if(!$tcn) { - $xpath = '//marc:datafield[@tag="010"]'; - ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; - $tcn_source = "LCCN"; - if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} - } - - if(!$tcn) { - $xpath = '//marc:datafield[@tag="035"]/subfield[@code="a"]'; - ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; - $tcn_source = "System Legacy"; - if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} - - if($tcn) { - $marcxml->documentElement->removeChild( - $marcxml->documentElement->findnodes( '//datafield[@tag="035"]' ) - ); - } - } - - return undef unless $tcn; - - if ($add_039) { - my $df = $marcxml->createElementNS( 'http://www.loc.gov/MARC21/slim', 'datafield'); - $df->setAttribute( tag => '039' ); - $df->setAttribute( ind1 => ' ' ); - $df->setAttribute( ind2 => ' ' ); - $marcxml->documentElement->appendChild( $df ); - - my $sfa = $marcxml->createElementNS( 'http://www.loc.gov/MARC21/slim', 'subfield'); - $sfa->setAttribute( code => 'a' ); - $sfa->appendChild( $marcxml->createTextNode( $tcn ) ); - $df->appendChild( $sfa ); - - my $sfb = $marcxml->createElementNS( 'http://www.loc.gov/MARC21/slim', 'subfield'); - $sfb->setAttribute( code => 'b' ); - $sfb->appendChild( $marcxml->createTextNode( $tcn_source ) ); - $df->appendChild( $sfb ); - } - - return $tcn; -} - - - -sub _tcn_exists { - my $editor = shift; - my $tcn = shift; - my $source = shift; - my $existing_rec = shift || 0; - - if(!$tcn) {return 0;} - - $logger->debug("tcn_exists search for tcn $tcn and source $source and id $existing_rec"); - - # XXX why does the source matter? -# my $req = $session->request( -# { tcn_value => $tcn, tcn_source => $source, deleted => 'f' } ); + my $res = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import( + $e, $xml, $source, $auto_tcn, $self->api_name =~ /override/); - my $recs = $editor->search_biblio_record_entry( - {tcn_value => $tcn, deleted => 'f', id => {'!=' => $existing_rec}}, {idlist =>1}); - - if(@$recs) { - $logger->debug("_tcn_exists is true for tcn : $tcn ($source)"); - return $recs->[0]; - } - - $logger->debug("_tcn_exists is false for tcn : $tcn ($source)"); - return 0; + $e->commit unless $U->event_code($res); + return $res; } - __PACKAGE__->register_method( method => "biblio_record_record_metadata", api_name => "open-ils.cat.biblio.record.metadata.retrieve", @@ -585,65 +335,6 @@ sub biblio_record_marc_cn { return \@res } -sub _get_id_by_userid { - - my @users = @_; - my @ids; - - my $session = OpenSRF::AppSession->create( "open-ils.cstore" ); - my $request = $session->request( - "open-ils.cstore.direct.actor.user.search.atomic", { usrname => \@users } ); - - $request->wait_complete; - my $response = $request->recv(); - if(!$request->complete) { - throw OpenSRF::EX::ERROR ("no response from cstore on user retrieve"); - } - - if(UNIVERSAL::isa( $response, "Error")){ - throw $response ($response); - } - - for my $u (@{$response->content}) { - next unless ref($u); - push @ids, $u->id(); - } - - $request->finish; - $session->disconnect; - $session->kill_me(); - - return @ids; -} - - -# commits metadata objects to the db -sub _update_record_metadata { - - my ($session, @docs ) = @_; - - for my $doc (@docs) { - - my $user_obj = $doc->{user}; - my $docid = $doc->{docid}; - - my $request = $session->request( - "open-ils.storage.direct.biblio.record_entry.retrieve", $docid ); - my $record = $request->gather(1); - - my ($id) = _get_id_by_userid($user_obj->usrname); - - $record->editor($id); - - $request = $session->request( - "open-ils.storage.direct.biblio.record_entry.update", $record ); - $request->gather(1); - } - - return 1; -} - - __PACKAGE__->register_method( method => "orgs_for_title", diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Cat/BibCommon.pm b/Open-ILS/src/perlmods/OpenILS/Application/Cat/BibCommon.pm new file mode 100644 index 0000000000..43434f089d --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Cat/BibCommon.pm @@ -0,0 +1,273 @@ +package OpenILS::Application::Cat::BibCommon; +use strict; use warnings; +use OpenILS::Utils::CStoreEditor q/:funcs/; +use OpenSRF::Utils::Logger qw($logger); +use OpenILS::Application::AppUtils; +use OpenILS::Utils::Fieldmapper; +use OpenILS::Const qw/:const/; +use OpenILS::Event; +my $U = 'OpenILS::Application::AppUtils'; +my $MARC_NAMESPACE = 'http://www.loc.gov/MARC21/slim'; + + +# --------------------------------------------------------------------------- +# Shared bib mangling code. Do not publish methods from here. +# --------------------------------------------------------------------------- + + +sub biblio_record_replace_marc { + my($class, $e, $recid, $newxml, $source, $fixtcn, $override) = @_; + + my $rec = $e->retrieve_biblio_record_entry($recid) + or return $e->die_event; + + # See if there is a different record in the database that has our TCN value + # If we're not updating the TCN, all we care about it the marcdoc + # XXX should .update even bother with the tcn_info if it's not going to replace it? + # there is the potential for returning a TCN_EXISTS event, even though no replacement happens + + my( $tcn, $tsource, $marcdoc, $evt); + + if($fixtcn or $override) { + + ($tcn, $tsource, $marcdoc, $evt) = + _find_tcn_info($e, $newxml, $override, $recid); + + return $evt if $evt; + + $rec->tcn_value($tcn) if ($tcn); + $rec->tcn_source($tsource); + + } else { + + $marcdoc = __make_marc_doc($newxml); + } + + + $rec->source(bib_source_from_name($source)) if $source; + $rec->editor($e->requestor->id); + $rec->edit_date('now'); + $rec->marc( $U->entityize( $marcdoc->documentElement->toString ) ); + $e->update_biblio_record_entry($rec) or return $e->die_event; + + # we don't care about the result, just fire off the request + my $ses = OpenSRF::AppSession->create('open-ils.ingest'); + $ses->request('open-ils.ingest.full.biblio.record', $recid); + + return $rec; +} + +sub biblio_record_xml_import { + my($class, $e, $xml, $source, $auto_tcn, $override) = @_; + + my( $evt, $tcn, $tcn_source, $marcdoc ); + + if( $auto_tcn ) { + # auto_tcn forces a blank TCN value so the DB will have to generate one for us + $marcdoc = __make_marc_doc($xml); + } else { + ( $tcn, $tcn_source, $marcdoc, $evt ) = _find_tcn_info($e, $xml, $override); + return $evt if $evt; + } + + $logger->info("user ".$e->requestor->id. + " creating new biblio entry with tcn=$tcn and tcn_source $tcn_source"); + + my $record = Fieldmapper::biblio::record_entry->new; + + $record->source(bib_source_from_name($source)) if $source; + $record->tcn_source($tcn_source); + $record->tcn_value($tcn) if ($tcn); + $record->creator($e->requestor->id); + $record->editor($e->requestor->id); + $record->create_date('now'); + $record->edit_date('now'); + $record->marc($U->entityize($marcdoc->documentElement->toString)); + + $record = $e->create_biblio_record_entry($record) or return $e->die_event; + $logger->info("marc create/import created new record ".$record->id); + + # we don't care about the result, just fire off the request + my $ses = OpenSRF::AppSession->create('open-ils.ingest'); + $ses->request('open-ils.ingest.full.biblio.record', $record->id); + + return $record; +} + +sub __make_marc_doc { + my $xml = shift; + my $marcxml = XML::LibXML->new->parse_string($xml); + $marcxml->documentElement->setNamespace($MARC_NAMESPACE, "marc", 1 ); + $marcxml->documentElement->setNamespace($MARC_NAMESPACE); + return $marcxml; +} + + +sub _find_tcn_info { + my $editor = shift; + my $xml = shift; + my $override = shift; + my $existing_rec = shift || 0; + + # parse the XML + my $marcxml = __make_marc_doc($xml); + + my $xpath = '//marc:controlfield[@tag="001"]'; + my $tcn = $marcxml->documentElement->findvalue($xpath); + $logger->info("biblio import located 001 (tcn) value of $tcn"); + + $xpath = '//marc:controlfield[@tag="003"]'; + my $tcn_source = $marcxml->documentElement->findvalue($xpath) || "System Local"; + + if(my $rec = _tcn_exists($editor, $tcn, $tcn_source, $existing_rec) ) { + + my $origtcn = $tcn; + $tcn = find_free_tcn( $marcxml, $editor, $existing_rec ); + + # if we're overriding, try to find a different TCN to use + if( $override ) { + + # XXX Create ALLOW_ALT_TCN permission check support + + $logger->info("tcn value $tcn already exists, attempting to override"); + + if(!$tcn) { + return ( + undef, + undef, + undef, + OpenILS::Event->new( + 'OPEN_TCN_NOT_FOUND', + payload => $marcxml->toString()) + ); + } + + } else { + + $logger->warn("tcn value $origtcn already exists in import/create"); + + # otherwise, return event + return ( + undef, + undef, + undef, + OpenILS::Event->new( + 'TCN_EXISTS', payload => { + dup_record => $rec, + tcn => $origtcn, + new_tcn => $tcn + } + ) + ); + } + } + + return ($tcn, $tcn_source, $marcxml); +} + +sub find_free_tcn { + + my $marcxml = shift; + my $editor = shift; + my $existing_rec = shift; + + my $add_039 = 0; + + my $xpath = '//marc:datafield[@tag="039"]/subfield[@code="a"]'; + my ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; + $xpath = '//marc:datafield[@tag="039"]/subfield[@code="b"]'; + my $tcn_source = $marcxml->documentElement->findvalue($xpath) || "System Local"; + + if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) { + $tcn = undef; + } else { + $add_039++; + } + + + if(!$tcn) { + $xpath = '//marc:datafield[@tag="020"]/subfield[@code="a"]'; + ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; + $tcn_source = "ISBN"; + if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} + } + + if(!$tcn) { + $xpath = '//marc:datafield[@tag="022"]/subfield[@code="a"]'; + ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; + $tcn_source = "ISSN"; + if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} + } + + if(!$tcn) { + $xpath = '//marc:datafield[@tag="010"]'; + ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; + $tcn_source = "LCCN"; + if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} + } + + if(!$tcn) { + $xpath = '//marc:datafield[@tag="035"]/subfield[@code="a"]'; + ($tcn) = $marcxml->documentElement->findvalue($xpath) =~ /(\w+)\s*$/o; + $tcn_source = "System Legacy"; + if(_tcn_exists($editor, $tcn, $tcn_source, $existing_rec)) {$tcn = undef;} + + if($tcn) { + $marcxml->documentElement->removeChild( + $marcxml->documentElement->findnodes( '//datafield[@tag="035"]' ) + ); + } + } + + return undef unless $tcn; + + if ($add_039) { + my $df = $marcxml->createElementNS( 'http://www.loc.gov/MARC21/slim', 'datafield'); + $df->setAttribute( tag => '039' ); + $df->setAttribute( ind1 => ' ' ); + $df->setAttribute( ind2 => ' ' ); + $marcxml->documentElement->appendChild( $df ); + + my $sfa = $marcxml->createElementNS( 'http://www.loc.gov/MARC21/slim', 'subfield'); + $sfa->setAttribute( code => 'a' ); + $sfa->appendChild( $marcxml->createTextNode( $tcn ) ); + $df->appendChild( $sfa ); + + my $sfb = $marcxml->createElementNS( 'http://www.loc.gov/MARC21/slim', 'subfield'); + $sfb->setAttribute( code => 'b' ); + $sfb->appendChild( $marcxml->createTextNode( $tcn_source ) ); + $df->appendChild( $sfb ); + } + + return $tcn; +} + + + +sub _tcn_exists { + my $editor = shift; + my $tcn = shift; + my $source = shift; + my $existing_rec = shift || 0; + + if(!$tcn) {return 0;} + + $logger->debug("tcn_exists search for tcn $tcn and source $source and id $existing_rec"); + + # XXX why does the source matter? +# my $req = $session->request( +# { tcn_value => $tcn, tcn_source => $source, deleted => 'f' } ); + + my $recs = $editor->search_biblio_record_entry( + {tcn_value => $tcn, deleted => 'f', id => {'!=' => $existing_rec}}, {idlist =>1}); + + if(@$recs) { + $logger->debug("_tcn_exists is true for tcn : $tcn ($source)"); + return $recs->[0]; + } + + $logger->debug("_tcn_exists is false for tcn : $tcn ($source)"); + return 0; +} + + -- 2.43.2