From 261d54b9135d358e8bbc1a85c50d4caef60a50d1 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Thu, 26 May 2022 10:24:50 +0100 Subject: [PATCH] Bug 30848: Add an ExpandCodedFields RecordProcessor filter This patch introduces a RecordProcessor filter for MARC::Record objects that replaces Koha codes with descriptions in the MARC::Record passed to the processor. Target usage: my $biblio = Koha::Biblios->find( $biblio_id, { prefetch => [ metadata ] } ); my $record = $biblio->metadata->record; my $processor = Koha::RecordProcessor->new( { filters => ('ExpandCodedFields'), options => { interface => 'opac', frameworkcode => $biblio->frameworkcode } } ); $processor->process( $record ); Test plan * Read the included unit test and confirm it makes sense and passes --- Koha/Filter/MARC/ExpandCodedFields.pm | 140 ++++++++++++++++++ admin/biblio_framework.pl | 2 + admin/marc_subfields_structure.pl | 2 + admin/marctagstructure.pl | 2 + .../Koha/Filter/ExpandCodedFields.t | 97 ++++++++++++ 5 files changed, 243 insertions(+) create mode 100644 Koha/Filter/MARC/ExpandCodedFields.pm create mode 100644 t/db_dependent/Koha/Filter/ExpandCodedFields.t diff --git a/Koha/Filter/MARC/ExpandCodedFields.pm b/Koha/Filter/MARC/ExpandCodedFields.pm new file mode 100644 index 0000000000..79b056431f --- /dev/null +++ b/Koha/Filter/MARC/ExpandCodedFields.pm @@ -0,0 +1,140 @@ +package Koha::Filter::MARC::ExpandCodedFields; + +# Copyright 2022 PTFS Europe +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +=head1 NAME + +Koha::Filter::MARC::ExpandCodedFields - Replaces AV codes with descriptions in MARC::Record objects. + +=head1 SYNOPSIS + + my $biblio = Koha::Biblios->find( + $biblio_id, + { prefetch => [ metadata ] } + ); + + my $record = $biblio->metadata->record; + + my $record_processor = Koha::RecordProcessor->new( + { + filters => ['ExpandCodedFields'], + options => { + interface => 'opac', + } + } + ); + + $record_processor->process($record); + +=head1 DESCRIPTION + +Filter to replace Koha AV codes in MARC::Records with their Koha AV descriptions. + +=cut + +use Modern::Perl; + +use C4::Biblio qw( GetAuthorisedValueDesc ); + +use Koha::Caches; + +use base qw(Koha::RecordProcessor::Base); +our $NAME = 'ExpandCodedFields'; + +=head2 filter + +Embed items into the MARC::Record object. + +=cut + +sub filter { + my $self = shift; + my $record = shift; + + return unless defined $record and ref($record) eq 'MARC::Record'; + + my $params = $self->params; + my $interface = $params->{options}->{interface} // 'opac'; + my $opac = $interface eq 'opac' ? 1 : 0; + my $frameworkcode = $params->{options}->{frameworkcode} // q{}; + + my $marcflavour = C4::Context->preference('marcflavour'); + my $coded_fields = _getCodedFields($frameworkcode); + for my $tag ( keys %$coded_fields ) { + for my $field ( $record->field($tag) ) { + my @new_subfields = (); + for my $subfield ( $field->subfields() ) { + my ( $letter, $value ) = @$subfield; + + # Replace the field value with the authorised value + # *except* for MARC21 field 942$n (suppression in opac) + if ( !( $tag eq '942' && $subfield->[0] eq 'n' ) + || $marcflavour eq 'UNIMARC' ) + { + $value = + GetAuthorisedValueDesc( $tag, $letter, $value, '', + $coded_fields, undef, $opac ) + if $coded_fields->{$tag}->{$letter}; + } + push( @new_subfields, $letter, $value ); + } + $field->replace_with( + MARC::Field->new( + $tag, $field->indicator(1), + $field->indicator(2), @new_subfields + ) + ); + } + } + + return $record; +} + +sub _getCodedFields { + my ($frameworkcode) = @_; + $frameworkcode //= ""; + + my $cache = Koha::Caches->get_instance(); + my $cache_key = "MarcCodedFields-$frameworkcode"; + my $cached = $cache->get_from_cache( $cache_key, { unsafe => 1 } ); + return $cached if $cached; + + my $coded_fields = { + map { + $_->tagfield => { + $_->tagsubfield => { + 'authorised_value' => $_->authorised_value + } + } + } Koha::MarcSubfieldStructures->search( + { + frameworkcode => $frameworkcode, + authorised_value => { '>' => '' } + }, + { + columns => [ 'tagfield', 'tagsubfield', 'authorised_value' ], + order_by => [ 'tagfield', 'tagsubfield' ] + } + )->as_list + }; + + $cache->set_in_cache( $cache_key, $coded_fields ); + return $coded_fields; +} + +1; diff --git a/admin/biblio_framework.pl b/admin/biblio_framework.pl index 32f2ac11eb..4712f0ea72 100755 --- a/admin/biblio_framework.pl +++ b/admin/biblio_framework.pl @@ -80,6 +80,7 @@ if ( $op eq 'add_form' ) { $cache->clear_from_cache("MarcStructure-1-$frameworkcode"); $cache->clear_from_cache("default_value_for_mod_marc-"); $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode"); + $cache->clear_from_cache("MarcCodedFields-$frameworkcode"); $op = 'list'; } elsif ( $op eq 'delete_confirm' ) { my $framework = Koha::BiblioFrameworks->find($frameworkcode); @@ -111,6 +112,7 @@ if ( $op eq 'add_form' ) { $cache->clear_from_cache("MarcStructure-1-$frameworkcode"); $cache->clear_from_cache("default_value_for_mod_marc-"); $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode"); + $cache->clear_from_cache("MarcCodedFields-$frameworkcode"); $op = 'list'; } diff --git a/admin/marc_subfields_structure.pl b/admin/marc_subfields_structure.pl index a2117eb912..a492e5c568 100755 --- a/admin/marc_subfields_structure.pl +++ b/admin/marc_subfields_structure.pl @@ -309,6 +309,7 @@ elsif ( $op eq 'add_validate' ) { $cache->clear_from_cache("MarcStructure-1-$frameworkcode"); $cache->clear_from_cache("default_value_for_mod_marc-"); $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode"); + $cache->clear_from_cache("MarcCodedFields-$frameworkcode"); print $input->redirect("/cgi-bin/koha/admin/marc_subfields_structure.pl?tagfield=$tagfield&frameworkcode=$frameworkcode"); exit; @@ -347,6 +348,7 @@ elsif ( $op eq 'delete_confirmed' ) { $cache->clear_from_cache("MarcStructure-1-$frameworkcode"); $cache->clear_from_cache("default_value_for_mod_marc-"); $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode"); + $cache->clear_from_cache("MarcCodedFields-$frameworkcode"); print $input->redirect("/cgi-bin/koha/admin/marc_subfields_structure.pl?tagfield=$tagfield&frameworkcode=$frameworkcode"); exit; diff --git a/admin/marctagstructure.pl b/admin/marctagstructure.pl index 7ce19f99ac..3514baca93 100755 --- a/admin/marctagstructure.pl +++ b/admin/marctagstructure.pl @@ -155,6 +155,7 @@ if ($op eq 'add_form') { $cache->clear_from_cache("MarcStructure-1-$frameworkcode"); $cache->clear_from_cache("default_value_for_mod_marc-"); $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode"); + $cache->clear_from_cache("MarcCodedFields-$frameworkcode"); print $input->redirect("/cgi-bin/koha/admin/marctagstructure.pl?searchfield=$tagfield&frameworkcode=$frameworkcode"); exit; # END $OP eq ADD_VALIDATE @@ -180,6 +181,7 @@ if ($op eq 'add_form') { $cache->clear_from_cache("MarcStructure-1-$frameworkcode"); $cache->clear_from_cache("default_value_for_mod_marc-"); $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode"); + $cache->clear_from_cache("MarcCodedFields-$frameworkcode"); $template->param( searchfield => $searchfield ); # END $OP eq DELETE_CONFIRMED ################## ITEMTYPE_CREATE ################################## diff --git a/t/db_dependent/Koha/Filter/ExpandCodedFields.t b/t/db_dependent/Koha/Filter/ExpandCodedFields.t new file mode 100644 index 0000000000..9200f0117f --- /dev/null +++ b/t/db_dependent/Koha/Filter/ExpandCodedFields.t @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::More tests => 1; + +use t::lib::TestBuilder; + +use C4::Biblio qw( GetMarcSubfieldStructure ); + +use Koha::Database; + +use Koha::AuthorisedValueCategory; +use Koha::Caches; + +use Koha::RecordProcessor; + +my $schema = Koha::Database->schema(); +my $builder = t::lib::TestBuilder->new(); + +subtest 'ExpandCodedFields tests' => sub { + + plan tests => 10; + + $schema->storage->txn_begin(); + + # Add a biblio + my $biblio = $builder->build_sample_biblio; + my $record = $biblio->metadata->record; + $record->field('942')->update( n => 1 ); + $record->append_fields( + MARC::Field->new( '590', '', '', a => 'CODE' ), + ); + + Koha::AuthorisedValueCategory->new({ category_name => 'TEST' })->store; + Koha::AuthorisedValue->new( + { + category => 'TEST', + authorised_value => 'CODE', + lib => 'Description should show', + lib_opac => 'Description should show OPAC' + } + )->store; + my $mss = Koha::MarcSubfieldStructures->find({tagfield => "590", tagsubfield => "a", frameworkcode => $biblio->frameworkcode }); + $mss->update({ authorised_value => "TEST" }); + + my $cache = Koha::Caches->get_instance; + $cache->clear_from_cache("MarcCodedFields-"); + + C4::Biblio::ModBiblio( $record, $biblio->biblionumber ); + $biblio = Koha::Biblios->find( $biblio->biblionumber); + $record = $biblio->metadata->record; + + is( $record->field('590')->subfield('a'), 'CODE', 'Record prior to filtering contains AV Code' ); + is( $record->field('942')->subfield('n'), 1, 'Record suppression is numeric prior to filtering' ); + + my $processor = Koha::RecordProcessor->new( + { + schema => 'MARC', + filters => ['ExpandCodedFields'], + } + ); + is( ref($processor), 'Koha::RecordProcessor', 'Created record processor with ExpandCodedFields filter' ); + + my $result = $processor->process( $record ); + is( ref($result), 'MARC::Record', 'It returns a reference to a MARC::Record object' ); + is( $result->field('590')->subfield('a'), 'Description should show OPAC', 'Returned record contains AV OPAC description (interface defaults to opac)' ); + is( $record->field('590')->subfield('a'), 'Description should show OPAC', 'Original record now contains AV OPAC description (interface defaults to opac)' ); + is( $record->field('942')->subfield('n'), 1, 'Record suppression is still numeric after filtering' ); + + # reset record for next test + $record = $biblio->metadata->record; + is( $record->field('590')->subfield('a'), 'CODE', 'Record reset contains AV Code' ); + + # set interface + $processor->options({ interface => 'intranet' }); + $result = $processor->process( $record ); + is( $record->field('590')->subfield('a'), 'Description should show', 'Original record now contains AV description (interface set to intranet)' ); + is( $record->field('942')->subfield('n'), 1, 'Item suppression field remains numeric after filtering' ); + + $schema->storage->txn_rollback(); +}; -- 2.20.1