Bugzilla – Attachment 157482 Details for
Bug 33036
Add route to merge bibliographic records
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
patch to implement the feature
0001-Bug-33036-REST-API-Merge-biblio-records-implements-m.patch (text/plain), 16.23 KB, created by
Zeno Tajoli
on 2023-10-19 21:09:20 UTC
(
hide
)
Description:
patch to implement the feature
Filename:
MIME Type:
Creator:
Zeno Tajoli
Created:
2023-10-19 21:09:20 UTC
Size:
16.23 KB
patch
obsolete
>From 942efc7bd02e64c7aa4b7f7b750c623273c2e3c4 Mon Sep 17 00:00:00 2001 >From: Zeno Tajoli <ztajoli@gmail.com> >Date: Thu, 19 Oct 2023 22:53:43 +0200 >Subject: [PATCH] Bug 33036: REST API: Merge biblio records implements merging > of records + attached items, subscriptions etc via the API as an alternative > to the web interface: cgi-bin/koha/cataloguing/merge.pl > >To test: > 1) you need an API user with the permissions "editcatalogue" > 2) two records: one to be merged into (with biblio_id, eg 262) and another one from > which to merge (with biblio_id_to_merge, eg 9) which will be deleted! > both records may/should have items, subscription, subscriptionhistory, serial, suggestions > orders and holds > 3) check both records via the web > 4) Apply patch > 5) execute API calls eg like > PUT /biblios/{biblio_id}/merge/{biblio_id_to_merge} > eg: > curl -s -u koha:koha -X POST "http://127.0.0.1:8081/api/v1/biblios/262/merge/9" > 6) the record with the id <biblio_id_to_merge> is deleted now, the record with <biblio_id> > has all items, etc attached, return code is 200, with the message {"message":"Successfuly merged 9 into 262"} > 7) optionally a full MARCXML record may be sent as body of the API call > curl -s -u koha:koha -X POST "http://127.0.0.1:8081/api/v1/biblios/262/merge/2" -d @marcfile.xml > 8) now also the content of the record with <biblio_id> is replaced with the content of the MARCXML file > 9) Go into intramet and do a search. Select two or (better) more record. > 10) Merge them; merge must be a success. > 11) Test with prove -v t/db_dependent/Koha/Biblio.t > >To do test with curl you canuse the MARCXML (MARC21) file attached in bugzilla. > >Sponsored-by: Technische Hochschule Wildau >Co-authored-by: domm@plix.at >Signed-off-by: Jan Kissig <jkissig@th-wildau.de> >--- > Koha/Biblio.pm | 88 +++++++++++++++++++++++++ > Koha/REST/V1/Biblios.pm | 60 +++++++++++++++++ > api/v1/swagger/paths/biblios_merge.yaml | 47 +++++++++++++ > api/v1/swagger/swagger.yaml | 2 + > cataloguing/merge.pl | 63 ++++-------------- > t/db_dependent/Koha/Biblio.t | 31 ++++++++- > 6 files changed, 237 insertions(+), 54 deletions(-) > create mode 100644 api/v1/swagger/paths/biblios_merge.yaml > >diff --git a/Koha/Biblio.pm b/Koha/Biblio.pm >index 9e2386ae98..7e68a58ad3 100644 >--- a/Koha/Biblio.pm >+++ b/Koha/Biblio.pm >@@ -24,6 +24,11 @@ use URI; > use URI::Escape qw( uri_escape_utf8 ); > > use C4::Koha qw( GetNormalizedISBN GetNormalizedUPC GetNormalizedOCLCNumber ); >+use C4::Biblio qw( DelBiblio AddBiblio ModBiblio ); >+use C4::Serials qw( CountSubscriptionFromBiblionumber ); >+use C4::Serials qw( CountSubscriptionFromBiblionumber ); >+use C4::Reserves qw( MergeHolds ); >+use C4::Acquisition qw( ModOrder GetOrdersByBiblionumber ); > > use Koha::Database; > use Koha::DateUtils qw( dt_from_string ); >@@ -1746,6 +1751,89 @@ sub opac_summary_html { > return $summary_html; > } > >+=head3 merge_with >+ >+ my $biblio = Koha::Biblios->find($biblionumber); >+ $biblio->merge_with(\@biblio_ids); >+ >+ This subroutine merges a list of bibliographic records into the bibliographic record. >+ This function DOES NOT CHANGE the bibliographic metadata of the record. But it links all >+ items, holds, subscriptions, serials issues and article_requests to the record. After doing changes >+ bibliographic records listed are deleted >+ >+=cut >+ >+sub merge_with { >+ my ( $self, $biblio_ids ) = @_; >+ my @biblio_ids_to_merge = @{$biblio_ids}; >+ my $dbh = C4::Context->dbh; >+ my $ref_biblionumber = $self->biblionumber; >+ # Ensure the keeper isn't in the list of records to merge >+ @biblio_ids_to_merge = grep { $_ ne $ref_biblionumber } @biblio_ids_to_merge; >+ my @worked_ids = (); >+ my %results = ( 'biblio_id' => $ref_biblionumber, 'merged_ids' => \@worked_ids, 'error' => '', 'status' => 1 ); >+ #my $biblio = Koha::Biblios->find($self->biblionumber); >+ >+ foreach (@biblio_ids_to_merge) { >+ my $bn_merge = $_; >+ eval { >+ my $from_biblio = Koha::Biblios->find($bn_merge); >+ $from_biblio->items->move_to_biblio($self); >+ $from_biblio->article_requests->update( { biblionumber => $ref_biblionumber }, { no_triggers => 1 } ); >+ >+ # Moving subscriptions from the other record to the reference record >+ my $sth_subscription = $dbh->prepare( " >+ UPDATE subscription SET biblionumber = ? WHERE biblionumber = ? >+ " ); >+ my $sth_subscriptionhistory = $dbh->prepare( " >+ UPDATE subscriptionhistory SET biblionumber = ? WHERE biblionumber = ? >+ " ); >+ my $subcount = CountSubscriptionFromBiblionumber($bn_merge); >+ if ( $subcount > 0 ) { >+ $sth_subscription->execute( $ref_biblionumber, $bn_merge ); >+ $sth_subscriptionhistory->execute( $ref_biblionumber, $bn_merge ); >+ } >+ >+ # Moving serials >+ my $sth_serial = $dbh->prepare( " >+ UPDATE serial SET biblionumber = ? WHERE biblionumber = ? >+ " ); >+ $sth_serial->execute( $ref_biblionumber, $bn_merge ); >+ >+ # Moving suggestions >+ my $sth_suggestions = $dbh->prepare( " >+ UPDATE suggestions SET biblionumber = ? WHERE biblionumber = ? >+ " ); >+ $sth_suggestions->execute( $ref_biblionumber, $bn_merge ); >+ # Moving orders (orders linked to items of frombiblio have already been moved by move_to_biblio) >+ my @allorders = GetOrdersByBiblionumber($bn_merge); >+ foreach my $myorder (@allorders) { >+ $myorder->{'biblionumber'} = $ref_biblionumber; >+ ModOrder($myorder); >+ # TODO : add error control (in ModOrder?) >+ } >+ >+ # Move holds >+ MergeHolds( $dbh, $ref_biblionumber, $bn_merge ); >+ }; >+ if ($@) { >+ $results{'error'} = $@; >+ $results{'status'} = 0; >+ return \%results; >+ } >+ # Deleting record >+ my $error = DelBiblio($bn_merge); >+ if ($error) { >+ $results{'error'} = $error; >+ $results{'status'} = 0; >+ return \%results; >+ } else { >+ push( @worked_ids, $bn_merge ); >+ } >+ } >+ return \%results; >+} >+ > =head2 Internal methods > > =head3 type >diff --git a/Koha/REST/V1/Biblios.pm b/Koha/REST/V1/Biblios.pm >index eef084bc2c..a6f10a0714 100644 >--- a/Koha/REST/V1/Biblios.pm >+++ b/Koha/REST/V1/Biblios.pm >@@ -860,4 +860,64 @@ sub list { > }; > } > >+=head3 merge >+ >+Controller function that handles merging two biblios. If an optional >+MARCXML is provided as the request body, this MARCXML replaces the >+bibliodata of the merge target biblio. Syntax format inside the request body >+must match with the Marc format used into Koha installation (MARC21 or UNIMARC) >+ >+=cut >+ >+sub merge { >+ my $c = shift->openapi->valid_input or return; >+ my $ref_biblionumber = $c->param('biblio_id'); >+ my $bn_merge = $c->param('biblio_id_to_merge'); >+ my $dbh = C4::Context->dbh; >+ >+ my $body = $c->req->body; >+ my $biblio = Koha::Biblios->find($ref_biblionumber); >+ if ( not defined $biblio ) { >+ return $c->render( >+ status => 404, >+ json => { error => sprintf( "[%s] biblio to merge into not found", $ref_biblionumber ) } >+ ); >+ } >+ >+ my $from_biblio = Koha::Biblios->find($bn_merge); >+ if ( not defined $from_biblio ) { >+ return $c->render( >+ status => 404, >+ # openapi => { error => sprintf("[%s] biblio to merge from not found", $bn_merge) } >+ json => { error => sprintf( "[%s] biblio to merge from not found", $bn_merge ) } >+ ); >+ } >+ my $results; >+ eval { >+ if ($body) { >+ my $record = MARC::Record->new_from_xml( $body, 'UTF-8', C4::Context->preference("marcflavour") ); >+ $record->leader( $biblio->metadata->record->leader() ); >+ ModBiblio( $record, $ref_biblionumber, $biblio->frameworkcode ); >+ } >+ my @biblio_ids_to_merge = ($bn_merge); >+ $results = $biblio->merge_with( \@biblio_ids_to_merge ); >+ }; >+ if ($@) { >+ return $c->render( status => 400, json => { error => $@ } ); >+ } else { >+ if ( $results->{'status'} == 1 ) { >+ return $c->render( >+ status => 200, >+ openapi => { >+ message => sprintf( "Successfully merged %s into %s", $bn_merge, $ref_biblionumber ), >+ merged => $bn_merge, >+ kept => $ref_biblionumber, >+ } >+ ); >+ } else { >+ return $c->render( status => 400, json => { error => $results->{'error'} } ); >+ } >+ } >+} >+ > 1; >diff --git a/api/v1/swagger/paths/biblios_merge.yaml b/api/v1/swagger/paths/biblios_merge.yaml >new file mode 100644 >index 0000000000..caafbd4022 >--- /dev/null >+++ b/api/v1/swagger/paths/biblios_merge.yaml >@@ -0,0 +1,47 @@ >+"/biblios/{biblio_id}/merge/{biblio_id_to_merge}": >+ post: >+ x-mojo-to: Biblios#merge >+ operationId: mergeBiblios >+ tags: >+ - merge_biblios >+ summary: Merge Biblios >+ parameters: >+ - name: biblio_id >+ in: path >+ description: Bilblionumber to be merged into >+ required: true >+ type: string >+ - name: biblio_id_to_merge >+ in: path >+ required: true >+ description: Biblionumber from which to merge >+ type: string >+ - $ref: "../swagger.yaml#/parameters/biblio_id_pp" >+ - $ref: "../swagger.yaml#/parameters/framework_id_header" >+ - $ref: "../swagger.yaml#/parameters/marc_schema_header" >+ responses: >+ '200': >+ description: Result message >+ '404': >+ description: Biblio not found >+ schema: >+ "$ref": "../swagger.yaml#/definitions/error" >+ '401': >+ description: Authentication required >+ schema: >+ "$ref": "../swagger.yaml#/definitions/error" >+ '403': >+ description: Access forbidden >+ schema: >+ "$ref": "../swagger.yaml#/definitions/error" >+ '500': >+ description: Internal server error >+ schema: >+ "$ref": "../swagger.yaml#/definitions/error" >+ '503': >+ description: Under maintenance >+ schema: >+ "$ref": "../swagger.yaml#/definitions/error" >+ x-koha-authorization: >+ permissions: >+ catalogue: "editcatalogue" >diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml >index 4c9e7bcb6e..a6c4623a7a 100644 >--- a/api/v1/swagger/swagger.yaml >+++ b/api/v1/swagger/swagger.yaml >@@ -205,6 +205,8 @@ paths: > $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups~1{item_group_id}~1items" > "/biblios/{biblio_id}/item_groups/{item_group_id}/items/{item_id}": > $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups~1{item_group_id}~1items~1{item_id}" >+ "/biblios/{biblio_id}/merge/{biblio_id_to_merge}": >+ $ref: "./paths/biblios_merge.yaml#/~1biblios~1{biblio_id}~1merge~1{biblio_id_to_merge}" > "/cash_registers/{cash_register_id}/cashups": > $ref: "./paths/cash_registers.yaml#/~1cash_registers~1{cash_register_id}~1cashups" > "/cashups/{cashup_id}": >diff --git a/cataloguing/merge.pl b/cataloguing/merge.pl >index 7fe218845e..5899f1b361 100755 >--- a/cataloguing/merge.pl >+++ b/cataloguing/merge.pl >@@ -86,33 +86,11 @@ if ($merge) { > # Rewriting the leader > my $biblio = Koha::Biblios->find($ref_biblionumber); > $record->leader($biblio->metadata->record->leader()); >- >+ #Take new framework code > my $frameworkcode = $input->param('frameworkcode'); >- > # Modifying the reference record > ModBiblio($record, $ref_biblionumber, $frameworkcode); > >- # Moving items and article requests from the other record to the reference record >- $biblio = $biblio->get_from_storage; >- foreach my $biblionumber (@biblionumbers) { >- my $from_biblio = Koha::Biblios->find($biblionumber); >- $from_biblio->items->move_to_biblio($biblio); >- $from_biblio->article_requests->update({ biblionumber => $ref_biblionumber }, { no_triggers => 1 }); >- } >- >- my $sth_subscription = $dbh->prepare(" >- UPDATE subscription SET biblionumber = ? WHERE biblionumber = ? >- "); >- my $sth_subscriptionhistory = $dbh->prepare(" >- UPDATE subscriptionhistory SET biblionumber = ? WHERE biblionumber = ? >- "); >- my $sth_serial = $dbh->prepare(" >- UPDATE serial SET biblionumber = ? WHERE biblionumber = ? >- "); >- my $sth_suggestions = $dbh->prepare(" >- UPDATE suggestions SET biblionumber = ? WHERE biblionumber = ? >- "); >- > my $report_header = {}; > foreach my $biblionumber ($ref_biblionumber, @biblionumbers) { > # build report >@@ -148,37 +126,18 @@ if ($merge) { > push @report_records, \%report_record; > } > >- foreach my $biblionumber (@biblionumbers) { >- # Moving subscriptions from the other record to the reference record >- my $subcount = CountSubscriptionFromBiblionumber($biblionumber); >- if ($subcount > 0) { >- $sth_subscription->execute($ref_biblionumber, $biblionumber); >- $sth_subscriptionhistory->execute($ref_biblionumber, $biblionumber); >+ my $rmerge; >+ eval { >+ my $newbiblio = Koha::Biblios->find($ref_biblionumber); >+ $rmerge = $newbiblio->merge_with(\@biblionumbers); >+ }; >+ if($@) >+ { push @errors, $@; >+ } else >+ { if ($rmerge->{'status'}==0) >+ { push @errors, $rmerge->{'error'}; > } >- >- # Moving serials >- $sth_serial->execute($ref_biblionumber, $biblionumber); >- >- # Moving suggestions >- $sth_suggestions->execute($ref_biblionumber, $biblionumber); >- >- # Moving orders (orders linked to items of frombiblio have already been moved by move_to_biblio) >- my @allorders = GetOrdersByBiblionumber($biblionumber); >- foreach my $myorder (@allorders) { >- $myorder->{'biblionumber'} = $ref_biblionumber; >- ModOrder ($myorder); >- # TODO : add error control (in ModOrder?) >- } >- >- # Deleting the other records >- if (scalar(@errors) == 0) { >- # Move holds >- MergeHolds($dbh, $ref_biblionumber, $biblionumber); >- my $error = DelBiblio($biblionumber); >- push @errors, $error if ($error); > } >-} >- > # Parameters > $template->param( > result => 1, >diff --git a/t/db_dependent/Koha/Biblio.t b/t/db_dependent/Koha/Biblio.t >index 13be09f466..bbe8d3639b 100755 >--- a/t/db_dependent/Koha/Biblio.t >+++ b/t/db_dependent/Koha/Biblio.t >@@ -17,7 +17,7 @@ > > use Modern::Perl; > >-use Test::More tests => 30; >+use Test::More tests => 31; > use Test::Exception; > use Test::Warn; > >@@ -486,13 +486,40 @@ subtest 'to_api() tests' => sub { > $schema->storage->txn_rollback; > }; > >+subtest 'merge of records' => sub { >+ plan tests => 1; >+ $schema->storage->txn_begin; >+ >+ #Three biblio >+ my $biblio1 = $builder->build_sample_biblio( { title => 'Title number 1' } ); >+ my $biblio2 = $builder->build_sample_biblio( { title => 'Title number 2' } ); >+ my $biblio3 = $builder->build_sample_biblio( { title => 'Title number 3' } ); >+ >+ # One items each >+ my $item1_1 = $builder->build_sample_item( { biblionumber => $biblio1->biblionumber, barcode => 'bar11' } ); >+ my $item2_1 = $builder->build_sample_item( { biblionumber => $biblio2->biblionumber, barcode => 'bar22' } ); >+ my $item3_1 = $builder->build_sample_item( { biblionumber => $biblio3->biblionumber, barcode => 'bar33' } ); >+ my $results = ''; >+ my @to_merge = ( $biblio2->biblionumber, $biblio3->biblionumber ); >+ eval { $results = $biblio1->merge_with( \@to_merge ); }; >+ if($@) { >+ is( 0, 1, "Not working. Error: " . $@ ); >+ } else { >+ my $items = $biblio1->items; >+ is( $items->count, 3, "After merge we have 3 items on first record" ); >+ } >+ #End work >+ #say Dumper $results; >+ $schema->storage->txn_rollback; >+}; >+ > subtest 'suggestions() tests' => sub { > > plan tests => 3; > > $schema->storage->txn_begin; > >- my $biblio = $builder->build_sample_biblio(); >+ my $biblio = $builder->build_sample_biblio(); > > is( ref($biblio->suggestions), 'Koha::Suggestions', 'Return type is correct' ); > >-- >2.30.2 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 33036
:
152436
|
152437
|
152438
|
152439
|
152440
|
152447
|
152472
|
152659
|
152678
|
157481
|
157482
|
157731
|
159011
|
159012
|
159103
|
159120
|
159191
|
159239
|
159826
|
159827
|
159828
|
159829
|
160302
|
160468
|
160469
|
160470
|
160471
|
160472