1 use strict; use warnings;
2 package OpenILS::Application::Cat;
3 use OpenILS::Application::AppUtils;
4 use OpenSRF::Application;
5 use OpenILS::Application::Cat::Utils;
6 use base qw/OpenSRF::Application/;
7 use Time::HiRes qw(time);
8 use OpenSRF::EX qw(:try);
10 use OpenILS::Utils::Fieldmapper;
13 use OpenILS::Utils::FlatXML;
15 my $apputils = "OpenILS::Application::AppUtils";
17 my $utils = "OpenILS::Application::Cat::Utils";
20 __PACKAGE__->register_method(
21 method => "biblio_record_tree_import",
22 api_name => "open-ils.cat.biblio.record.tree.import",
25 sub biblio_record_tree_import {
26 my( $self, $client, $user_session, $tree) = @_;
27 my $user_obj = $apputils->check_user_session($user_session);
29 warn "importing new record " . Dumper($tree) . "\n";
31 my $nodeset = $utils->tree2nodeset($tree);
32 warn "turned into nodeset " . Dumper($nodeset) . "\n";
34 # copy the doc so that we can mangle the namespace.
35 my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml($nodeset);
36 my $copy_marcxml = XML::LibXML->new->parse_string($marcxml->toString);
38 $marcxml->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
42 warn "Starting db session in import\n";
43 my $session = $apputils->start_db_session();
44 my $source = 2; # system local source
46 my $xpath = '//controlfield[@tag="001"]';
47 $tcn = $marcxml->documentElement->findvalue($xpath);
48 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
49 my $tcn_source = "External";
53 $xpath = '//datafield[@tag="020"]';
54 $tcn = $marcxml->documentElement->findvalue($xpath);
56 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
60 $xpath = '//datafield[@tag="022"]';
61 $tcn = $marcxml->documentElement->findvalue($xpath);
63 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
67 $xpath = '//datafield[@tag="010"]';
68 $tcn = $marcxml->documentElement->findvalue($xpath);
70 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
74 $xpath = '//datafield[@tag="035"]';
75 $tcn = $marcxml->documentElement->findvalue($xpath);
76 $tcn_source = "System";
77 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
80 warn "Record import with tcn: $tcn and source $tcn_source\n";
82 my $record = Fieldmapper::biblio::record_entry->new;
84 $record->source($source);
85 $record->tcn_source($tcn_source);
86 $record->tcn_value($tcn);
87 $record->creator($user_obj->id);
88 $record->editor($user_obj->id);
89 $record->marc($copy_marcxml->toString);
92 my $req = $session->request(
93 "open-ils.storage.direct.biblio.record_entry.create",
95 my $id = $req->gather(1);
97 my $wreq = $session->request("open-ils.worm.wormize", $id);
100 $apputils->commit_db_session($session);
102 return $self->biblio_record_tree_retrieve($client, $id);
109 if(!$tcn) {return 0;}
111 my $req = $session->request(
112 "open-ils.storage.direct.biblio.record_entry.search.tcn_value.atomic",
114 my $recs = $req->gather(1);
116 if($recs and $recs->[0]) {
124 __PACKAGE__->register_method(
125 method => "biblio_record_tree_retrieve",
126 api_name => "open-ils.cat.biblio.record.tree.retrieve",
129 sub biblio_record_tree_retrieve {
131 my( $self, $client, $recordid ) = @_;
133 my $name = "open-ils.storage.direct.biblio.record_entry.retrieve";
134 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
135 my $request = $session->request( $name, $recordid );
136 my $marcxml = $request->gather(1);
139 throw OpenSRF::EX::ERROR
140 ("No record in database with id $recordid");
143 $session->disconnect();
146 warn "turning into nodeset\n";
147 my $nodes = OpenILS::Utils::FlatXML->new()->xml_to_nodeset( $marcxml->marc );
148 warn "turning nodeset into tree\n";
149 my $tree = $utils->nodeset2tree( $nodes->nodeset );
151 $tree->owner_doc( $marcxml->id() );
153 warn "returning tree\n";
158 __PACKAGE__->register_method(
159 method => "biblio_record_tree_commit",
160 api_name => "open-ils.cat.biblio.record.tree.commit",
161 argc => 3, #(session_id, biblio_tree )
162 note => "Walks the tree and commits any changed nodes " .
163 "adds any new nodes, and deletes any deleted nodes",
166 sub biblio_record_tree_commit {
168 my( $self, $client, $user_session, $tree ) = @_;
170 throw OpenSRF::EX::InvalidArg
171 ("Not enough args to to open-ils.cat.biblio.record.tree.commit")
172 unless ( $user_session and $tree );
175 OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
178 my $docid = $tree->owner_doc();
179 my $session = OpenILS::Application::AppUtils->start_db_session();
181 warn "Retrieving biblio record from storage for update\n";
183 my $req1 = $session->request(
184 "open-ils.storage.direct.biblio.record_entry.batch.retrieve",
186 my $biblio = $req1->gather(1);
188 warn "retrieved doc $docid\n";
191 # turn the tree into a nodeset
192 my $nodeset = $utils->tree2nodeset($tree);
193 $nodeset = $utils->clean_nodeset($nodeset);
195 if(!defined($docid)) { # be sure
196 for my $node (@$nodeset) {
197 $docid = $node->owner_doc();
198 last if defined($docid);
202 # turn the nodeset into a doc
203 my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml( $nodeset );
205 $biblio->marc( $marcxml->toString() );
207 warn "Starting db session\n";
209 my $x = _update_record_metadata( $session, { user => $user_obj, docid => $docid } );
210 OpenILS::Application::AppUtils->rollback_db_session($session) unless $x;
212 warn "Sending updated doc $docid to db\n";
213 my $req = $session->request( "open-ils.storage.direct.biblio.record_entry.update", $biblio );
216 my $status = $req->recv();
217 if( !$status || $status->isa("Error") || ! $status->content) {
218 OpenILS::Application::AppUtils->rollback_db_session($session);
219 if($status->isa("Error")) { throw $status ($status); }
220 throw OpenSRF::EX::ERROR ("Error updating biblio record");
224 # Send the doc to the wormer for wormizing
225 warn "Starting worm session\n";
230 my $wreq = $session->request( "open-ils.worm.wormize", $docid );
237 warn "wormizing failed, rolling back\n";
238 OpenILS::Application::AppUtils->rollback_db_session($session);
240 if($e) { throw $e ($e); }
241 throw OpenSRF::EX::ERROR ("Wormizing Failed for $docid" );
244 warn "Committing db session...\n";
245 OpenILS::Application::AppUtils->commit_db_session( $session );
247 $nodeset = OpenILS::Utils::FlatXML->new()->xmldoc_to_nodeset($marcxml);
248 $tree = $utils->nodeset2tree($nodeset->nodeset);
249 $tree->owner_doc($docid);
251 # $client->respond_complete($tree);
253 warn "Done wormizing\n";
256 #warn "Returning tree:\n";
265 __PACKAGE__->register_method(
266 method => "biblio_record_record_metadata",
267 api_name => "open-ils.cat.biblio.record.metadata.retrieve",
268 argc => 1, #(session_id, biblio_tree )
269 note => "Walks the tree and commits any changed nodes " .
270 "adds any new nodes, and deletes any deleted nodes",
273 sub biblio_record_record_metadata {
274 my( $self, $client, @ids ) = @_;
276 if(!@ids){return undef;}
278 my $session = OpenSRF::AppSession->create("open-ils.storage");
279 my $request = $session->request(
280 "open-ils.storage.direct.biblio.record_entry.batch.retrieve", @ids );
284 while( my $response = $request->recv() ) {
287 throw OpenSRF::EX::ERROR ("No Response from Storage");
289 if($response->isa("Error")) {
290 throw $response ($response->stringify);
293 my $record_entry = $response->content;
295 my $creator = $record_entry->creator;
296 my $editor = $record_entry->editor;
298 ($creator, $editor) = _get_userid_by_id($creator, $editor);
300 $record_entry->creator( $creator );
301 $record_entry->editor( $editor );
303 push @$results, $record_entry;
308 $session->disconnect();
316 sub _get_userid_by_id {
321 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
322 my $request = $session->request(
323 "open-ils.storage.direct.actor.user.batch.retrieve.atomic", @ids );
325 $request->wait_complete;
326 my $response = $request->recv();
327 if(!$request->complete) { return undef; }
329 if($response->isa("Error")){
330 throw $response ($response);
333 for my $u (@{$response->content}) {
335 push @users, $u->usrname;
339 $session->disconnect;
345 sub _get_id_by_userid {
350 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
351 my $request = $session->request(
352 "open-ils.storage.direct.actor.user.search.usrname.atomic", @users );
354 $request->wait_complete;
355 my $response = $request->recv();
356 if(!$request->complete) {
357 throw OpenSRF::EX::ERROR ("no response from storage on user retrieve");
360 if(UNIVERSAL::isa( $response, "Error")){
361 throw $response ($response);
364 for my $u (@{$response->content}) {
370 $session->disconnect;
377 # commits metadata objects to the db
378 sub _update_record_metadata {
380 my ($session, @docs ) = @_;
382 for my $doc (@docs) {
384 my $user_obj = $doc->{user};
385 my $docid = $doc->{docid};
387 warn "Updating metata for doc $docid\n";
389 my $request = $session->request(
390 "open-ils.storage.direct.biblio.record_entry.retrieve", $docid );
391 my $record = $request->gather(1);
393 warn "retrieved record\n";
394 my ($id) = _get_id_by_userid($user_obj->usrname);
396 warn "got $id from _get_id_by_userid\n";
397 $record->editor($id);
399 warn "Grabbed the record, updating and moving on\n";
401 $request = $session->request(
402 "open-ils.storage.direct.biblio.record_entry.update", $record );
406 warn "committing metarecord update\n";
413 __PACKAGE__->register_method(
414 method => "orgs_for_title",
415 api_name => "open-ils.cat.actor.org_unit.retrieve_by_title"
419 my( $self, $client, $record_id ) = @_;
421 my $vols = $apputils->simple_scalar_request(
423 "open-ils.storage.direct.asset.call_number.search.record.atomic",
426 my $orgs = { map {$_->owning_lib => 1 } @$vols };
427 return [ keys %$orgs ];
432 __PACKAGE__->register_method(
433 method => "retrieve_copies",
434 api_name => "open-ils.cat.asset.copy_tree.retrieve");
436 __PACKAGE__->register_method(
437 method => "retrieve_copies",
438 api_name => "open-ils.cat.asset.copy_tree.global.retrieve");
440 # user_session may be null/undef
441 sub retrieve_copies {
443 my( $self, $client, $user_session, $docid, @org_ids ) = @_;
445 if(ref($org_ids[0])) { @org_ids = @{$org_ids[0]}; }
449 warn " $$ retrieving copy tree for orgs @org_ids and doc $docid at " . time() . "\n";
451 # grabbing copy trees should be available for everyone..
452 if(!@org_ids and $user_session) {
454 OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
455 @org_ids = ($user_obj->home_ou);
458 if( $self->api_name =~ /global/ ) {
459 warn "performing global copy_tree search for $docid\n";
460 return _build_volume_list( { record => $docid } );
465 for my $orgid (@org_ids) {
466 my $vols = _build_volume_list(
467 { record => $docid, owning_lib => $orgid } );
468 warn "Volumes built for org $orgid\n";
469 push( @all_vols, @$vols );
472 warn " $$ Finished copy_tree at " . time() . "\n";
480 sub _build_volume_list {
481 my $search_hash = shift;
483 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
486 my $request = $session->request(
487 "open-ils.storage.direct.asset.call_number.search.atomic", $search_hash );
489 my $vols = $request->gather(1);
492 for my $volume (@$vols) {
494 warn "Grabbing copies for volume: " . $volume->id . "\n";
495 my $creq = $session->request(
496 "open-ils.storage.direct.asset.copy.search.call_number.atomic",
498 my $copies = $creq->gather(1);
500 $volume->copies($copies);
502 push( @volumes, $volume );
506 $session->disconnect();
512 # -----------------------------------------------------------------
513 # Fleshed volume tree batch add/update. This does everything a
514 # volume tree could want, add, update, delete
515 # -----------------------------------------------------------------
516 __PACKAGE__->register_method(
517 method => "volume_tree_fleshed_update",
518 api_name => "open-ils.cat.asset.volume_tree.fleshed.batch.update",
520 sub volume_tree_fleshed_update {
522 my( $self, $client, $user_session, $volumes ) = @_;
523 return undef unless $volumes;
524 my $user_obj = $apputils->check_user_session($user_session);
526 my $session = $apputils->start_db_session();
527 warn "Looping on volumes in fleshed volume tree update\n";
529 # cycle through the volumes provided and update/create/delete where necessary
530 for my $volume (@$volumes) {
532 warn "updating volume " . $volume->id . "\n";
534 my $update_copy_list = $volume->copies;
537 if( $volume->isdeleted) {
538 my $status = _delete_volume($session, $volume);
540 throw OpenSRF::EX::ERROR
541 ("Volume delete failed for volume " . $volume->id);
544 } elsif( $volume->isnew ) {
547 $volume->editor($user_obj->id);
548 $volume->creator($user_obj->id);
549 $volume = _add_volume($session, $volume);
551 } elsif( $volume->ischanged ) {
553 $volume->editor($user_obj->id);
554 _update_volume($session, $volume);
558 if( ! $volume->isdeleted ) {
559 for my $copy (@{$update_copy_list}) {
561 $copy->editor($user_obj->id);
562 warn "updating copy for volume " . $volume->id . "\n";
567 $copy->call_number($volume->id);
568 $copy->creator($user_obj->id);
569 $copy = _fleshed_copy_update($session,$copy,$user_obj->id);
571 } elsif( $copy->ischanged ) {
572 $copy->call_number($volume->id);
573 $copy = _fleshed_copy_update($session, $copy, $user_obj->id);
575 } elsif( $copy->isdeleted ) {
576 warn "Deleting copy " . $copy->id . " for volume " . $volume->id . "\n";
577 my $status = _fleshed_copy_update($session, $copy, $user_obj->id);
578 warn "Copy delete returned a status of $status\n";
583 $apputils->commit_db_session($session);
584 return scalar(@$volumes);
589 my( $session, $volume ) = @_;
591 #$volume = _find_volume($session, $volume);
592 warn "Deleting volume " . $volume->id . "\n";
594 my $copies = $session->request(
595 "open-ils.storage.direct.asset.copy.search.call_number.atomic",
596 $volume->id )->gather(1);
598 throw OpenSRF::EX::ERROR
599 ("Cannot remove volume with copies attached");
602 my $req = $session->request(
603 "open-ils.storage.direct.asset.call_number.delete",
605 return $req->gather(1);
610 my($session, $volume) = @_;
611 my $req = $session->request(
612 "open-ils.storage.direct.asset.call_number.update",
614 my $status = $req->gather(1);
619 my($session, $volume) = @_;
621 my $request = $session->request(
622 "open-ils.storage.direct.asset.call_number.create", $volume );
624 my $id = $request->gather(1);
627 OpenILS::Application::AppUtils->rollback_db_session($session);
628 throw OpenSRF::EX::ERROR (" * -> Error creating new volume");
632 warn "received new volume id: $id\n";
640 __PACKAGE__->register_method(
641 method => "fleshed_copy_update",
642 api_name => "open-ils.cat.asset.copy.fleshed.batch.update",
645 sub fleshed_copy_update {
646 my($self, $client, $user_session, $copies) = @_;
648 my $user_obj = $apputils->check_user_session($user_session);
649 my $session = $apputils->start_db_session();
651 for my $copy (@$copies) {
652 _fleshed_copy_update($session, $copy, $user_obj->id);
655 $apputils->commit_db_session($session);
662 my($session, $copy) = @_;
663 warn "Deleting copy " . $copy->id . "\n";
664 my $request = $session->request(
665 "open-ils.storage.direct.asset.copy.delete",
667 return $request->gather(1);
671 my($session, $copy) = @_;
673 my $request = $session->request(
674 "open-ils.storage.direct.asset.copy.create",
676 my $id = $request->gather(1);
679 throw OpenSRF::EX::ERROR
680 ("Unable to create new copy " . Dumper($copy));
683 warn "Created copy " . $copy->id . "\n";
690 my($session, $copy) = @_;
691 my $request = $session->request(
692 "open-ils.storage.direct.asset.copy.update", $copy );
693 my $status = $request->gather(1);
694 warn "Updated copy " . $copy->id . "\n";
699 # -----------------------------------------------------------------
700 # Creates/Updates/Deletes a fleshed asset.copy.
701 # adds/deletes copy stat_cat maps where necessary
702 # -----------------------------------------------------------------
703 sub _fleshed_copy_update {
704 my($session, $copy, $editor) = @_;
706 my $stat_cat_entries = $copy->stat_cat_entries;
707 $copy->editor($editor);
709 # in case we're fleshed
710 if(ref($copy->status)) {$copy->status( $copy->status->id ); }
711 if(ref($copy->location)) {$copy->location( $copy->location->id ); }
712 if(ref($copy->circ_lib)) {$copy->circ_lib( $copy->circ_lib->id ); }
714 warn "Updating copy " . Dumper($copy) . "\n";
716 if( $copy->isdeleted ) {
717 return _delete_copy($session, $copy);
718 } elsif( $copy->isnew ) {
719 $copy = _create_copy($session, $copy);
720 } elsif( $copy->ischanged ) {
721 _update_copy($session, $copy);
725 if(!@$stat_cat_entries) { return 1; }
727 my $stat_maps = $session->request(
728 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.search.owning_copy.atomic",
729 $copy->id )->gather(1);
731 if(!$copy->isnew) { _delete_stale_maps($session, $stat_maps, $copy); }
733 # go through the stat cat update/create process
734 for my $stat_entry (@{$stat_cat_entries}){
735 _copy_update_stat_cats( $session, $copy, $stat_maps, $stat_entry );
742 # -----------------------------------------------------------------
743 # Deletes stat maps attached to this copy in the database that
744 # are no longer attached to the current copy
745 # -----------------------------------------------------------------
746 sub _delete_stale_maps {
747 my( $session, $stat_maps, $copy) = @_;
749 warn "Deleting stale stat maps for copy " . $copy->id . "\n";
750 for my $map (@$stat_maps) {
751 # if there is no stat cat entry on the copy who's id matches the
752 # current map's id, remove the map from the database
753 if(! grep { $_->id == $map->stat_cat_entry } @{$copy->stat_cat_entries} ) {
754 my $req = $session->request(
755 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.delete", $map );
764 # -----------------------------------------------------------------
765 # Searches the stat maps to see if '$entry' already exists on
766 # the given copy. If it does not, a new stat map is created
767 # for the given entry and copy
768 # -----------------------------------------------------------------
769 sub _copy_update_stat_cats {
770 my ( $session, $copy, $stat_maps, $entry ) = @_;
772 warn "Updating stat maps for copy " . $copy->id . "\n";
774 # see if this map already exists
775 for my $map (@$stat_maps) {
776 if( $map->stat_cat_entry == $entry->id ) {return;}
779 warn "Creating new stat map for stat " .
780 $entry->stat_cat . " and copy " . $copy->id . "\n";
783 my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
785 $new_map->stat_cat( $entry->stat_cat );
786 $new_map->stat_cat_entry( $entry->id );
787 $new_map->owning_copy( $copy->id );
789 warn "New map is " . Dumper($new_map) . "\n";
791 my $request = $session->request(
792 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.create",
794 my $status = $request->gather(1);
795 warn "created new map with id $status\n";