1 package Koha::SimpleMARC;
3 # Copyright 2009 Kyle M. Hall <kyle.m.hall@gmail.com>
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
25 our @ISA = qw(Exporter);
26 our %EXPORT_TAGS = ( 'all' => [ qw(
30 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
37 copy_and_replace_field
47 SimpleMARC - Perl module for making simple MARC record alterations.
55 SimpleMARC is designed to make writing scripts
56 to modify MARC records simple and easy.
58 Every function in the modules requires a
59 MARC::Record object as its first parameter.
63 Kyle Hall <lt>kyle.m.hall@gmail.com<gt>
65 =head1 COPYRIGHT AND LICENSE
67 Copyright (C) 2009 by Kyle Hall
69 This library is free software; you can redistribute it and/or modify
70 it under the same terms as Perl itself, either Perl version 5.8.7 or,
71 at your option, any later version of Perl 5 you may have available.
77 copy_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex[, $n ] ] );
79 Copies a value from one field to another. If a regular expression ( $regex ) is supplied,
80 the value will be transformed by the given regex before being copied into the new field.
81 Example: $regex = { search => 'Old Text', replace => 'Replacement Text', modifiers => 'g' };
83 If $n is passed, copy_field will only copy the Nth field of the list of fields.
84 E.g. $n = 1 will only use the first field's value, $n = 2 will use only the 2nd field's value.
90 my $record = $params->{record};
91 my $fromFieldName = $params->{from_field};
92 my $fromSubfieldName = $params->{from_subfield};
93 my $toFieldName = $params->{to_field};
94 my $toSubfieldName = $params->{to_subfield};
95 my $regex = $params->{regex};
96 my $field_numbers = $params->{field_numbers} // [];
98 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
101 if ( not $fromSubfieldName
102 or $fromSubfieldName eq ''
103 or not $toSubfieldName
104 or $toSubfieldName eq '' ) {
107 from_field => $fromFieldName,
108 to_field => $toFieldName,
110 field_numbers => $field_numbers,
117 from_field => $fromFieldName,
118 from_subfield => $fromSubfieldName,
119 to_field => $toFieldName,
120 to_subfield => $toSubfieldName,
122 field_numbers => $field_numbers,
129 sub copy_and_replace_field {
131 my $record = $params->{record};
132 my $fromFieldName = $params->{from_field};
133 my $fromSubfieldName = $params->{from_subfield};
134 my $toFieldName = $params->{to_field};
135 my $toSubfieldName = $params->{to_subfield};
136 my $regex = $params->{regex};
137 my $field_numbers = $params->{field_numbers} // [];
139 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
142 if ( !defined $fromSubfieldName or $fromSubfieldName eq ''
143 or !defined $toSubfieldName or $toSubfieldName eq ''
147 from_field => $fromFieldName,
148 to_field => $toFieldName,
150 field_numbers => $field_numbers,
157 from_field => $fromFieldName,
158 from_subfield => $fromSubfieldName,
159 to_field => $toFieldName,
160 to_subfield => $toSubfieldName,
162 field_numbers => $field_numbers,
171 my $record = $params->{record};
172 my $fieldName = $params->{field};
173 my $subfieldName = $params->{subfield};
174 my @values = @{ $params->{values} };
175 my $field_numbers = $params->{field_numbers} // [];
177 if ( ! ( $record && $fieldName ) ) { return; }
179 if ( not defined $subfieldName or $subfieldName eq '' ) {
180 # FIXME I'm not sure the actual implementation is correct.
181 die "This action is not implemented yet";
182 #_update_field({ record => $record, field => $fieldName, values => \@values });
184 _update_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, values => \@values, field_numbers => $field_numbers });
193 subfield => $subfieldName,
195 field_numbers => $field_numbers,
198 Adds a new field/subfield with supplied value(s).
199 This function always add a new field as opposed to 'update_field' which will
200 either update if field exists and add if it does not.
207 my $record = $params->{record};
208 my $fieldName = $params->{field};
209 my $subfieldName = $params->{subfield};
210 my @values = @{ $params->{values} };
211 my $field_numbers = $params->{field_numbers} // [];
213 if ( ! ( $record && $fieldName ) ) { return; }
214 if ( $fieldName > 10 ) {
215 foreach my $value ( @values ) {
216 my $field = MARC::Field->new( $fieldName, '', '', "$subfieldName" => $value );
217 $record->append_fields( $field );
220 foreach my $value ( @values ) {
221 my $field = MARC::Field->new( $fieldName, $value );
222 $record->append_fields( $field );
229 my $record = $params->{record};
230 my $fieldName = $params->{field};
231 my @values = @{ $params->{values} };
234 if ( my @fields = $record->field( $fieldName ) ) {
235 @values = ($values[0]) x scalar( @fields )
237 foreach my $field ( @fields ) {
238 $field->update( $values[$i++] );
241 ## Field does not exists, create it
242 if ( $fieldName < 10 ) {
243 foreach my $value ( @values ) {
244 my $field = MARC::Field->new( $fieldName, $value );
245 $record->append_fields( $field );
248 warn "Invalid operation, trying to add a new field without subfield";
253 sub _update_subfield {
255 my $record = $params->{record};
256 my $fieldName = $params->{field};
257 my $subfieldName = $params->{subfield};
258 my @values = @{ $params->{values} };
259 my $dont_erase = $params->{dont_erase};
260 my $field_numbers = $params->{field_numbers} // [];
263 my @fields = $record->field( $fieldName );
265 if ( @$field_numbers ) {
266 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
270 unless ( $dont_erase ) {
271 @values = ($values[0]) x scalar( @fields )
273 foreach my $field ( @fields ) {
274 $field->update( "$subfieldName" => $values[$i++] );
277 if ( $i <= scalar ( @values ) - 1 ) {
278 foreach my $field ( @fields ) {
279 foreach my $j ( $i .. scalar( @values ) - 1) {
280 $field->add_subfields( "$subfieldName" => $values[$j] );
285 ## Field does not exist, create it.
286 foreach my $value ( @values ) {
287 my $field = MARC::Field->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
288 $record->append_fields( $field );
295 my @values = read_field( $record, $fieldName[, $subfieldName, [, $n ] ] );
297 Returns an array of field values for the given field and subfield
299 If $n is given, it will return only the $nth value of the array.
300 E.g. If $n = 1, it return the 1st value, if $n = 3, it will return the 3rd value.
306 my $record = $params->{record};
307 my $fieldName = $params->{field};
308 my $subfieldName = $params->{subfield};
309 my $field_numbers = $params->{field_numbers} // [];
311 if ( not defined $subfieldName or $subfieldName eq '' ) {
312 _read_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
314 _read_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
320 my $record = $params->{record};
321 my $fieldName = $params->{field};
322 my $field_numbers = $params->{field_numbers} // [];
324 my @fields = $record->field( $fieldName );
326 return unless @fields;
328 return map { $_->data() } @fields
332 if ( @$field_numbers ) {
333 for my $field_number ( @$field_numbers ) {
334 if ( $field_number <= scalar( @fields ) ) {
335 for my $sf ( $fields[$field_number - 1]->subfields ) {
336 push @values, $sf->[1];
341 foreach my $field ( @fields ) {
342 for my $sf ( $field->subfields ) {
343 push @values, $sf->[1];
353 my $record = $params->{record};
354 my $fieldName = $params->{field};
355 my $subfieldName = $params->{subfield};
356 my $field_numbers = $params->{field_numbers} // [];
358 my @fields = $record->field( $fieldName );
360 return unless @fields;
363 foreach my $field ( @fields ) {
364 my @sf = $field->subfield( $subfieldName );
365 push( @values, @sf );
368 if ( @values and @$field_numbers ) {
369 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
377 @field_numbers = field_exists( $record, $fieldName[, $subfieldName ]);
379 Returns the field numbers or an empty array.
385 my $record = $params->{record};
386 my $fieldName = $params->{field};
387 my $subfieldName = $params->{subfield};
389 if ( ! $record ) { return; }
391 my @field_numbers = ();
392 my $current_field_number = 1;
393 for my $field ( $record->field( $fieldName ) ) {
394 if ( $subfieldName ) {
395 push @field_numbers, $current_field_number
396 if $field->subfield( $subfieldName );
398 push @field_numbers, $current_field_number;
400 $current_field_number++;
403 return \@field_numbers;
408 $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
410 Returns true if the field equals the given value, false otherwise.
412 If a regular expression ( $regex ) is supplied, the value will be compared using
413 the given regex. Example: $regex = 'sought_text'
419 my $record = $params->{record};
420 my $value = $params->{value};
421 my $fieldName = $params->{field};
422 my $subfieldName = $params->{subfield};
423 my $is_regex = $params->{is_regex};
425 if ( ! $record ) { return; }
427 my @field_numbers = ();
428 my $current_field_number = 1;
429 FIELDS: for my $field ( $record->field( $fieldName ) ) {
431 if ( $field->is_control_field ) {
432 push @subfield_values, $field->data;
436 ? $field->subfield($subfieldName)
437 : map { $_->[1] } $field->subfields;
440 SUBFIELDS: for my $subfield_value ( @subfield_values ) {
443 $is_regex and $subfield_value =~ m/$value/
445 $subfield_value eq $value
448 push @field_numbers, $current_field_number;
452 $current_field_number++;
455 return \@field_numbers;
460 move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex [, $n ] ] );
462 Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
463 the value will be transformed by the given regex before being moved into the new field.
464 Example: $regex = 's/Old Text/Replacement Text/'
466 If $n is passed, only the Nth field will be moved. $n = 1
467 will move the first repeatable field, $n = 3 will move the third.
473 my $record = $params->{record};
474 my $fromFieldName = $params->{from_field};
475 my $fromSubfieldName = $params->{from_subfield};
476 my $toFieldName = $params->{to_field};
477 my $toSubfieldName = $params->{to_subfield};
478 my $regex = $params->{regex};
479 my $field_numbers = $params->{field_numbers} // [];
481 if ( !defined $fromSubfieldName
482 or $fromSubfieldName eq ''
483 or !defined $toSubfieldName
484 or $toSubfieldName eq '' ) {
487 from_field => $fromFieldName,
488 to_field => $toFieldName,
490 field_numbers => $field_numbers,
497 from_field => $fromFieldName,
498 from_subfield => $fromSubfieldName,
499 to_field => $toFieldName,
500 to_subfield => $toSubfieldName,
502 field_numbers => $field_numbers,
511 _delete_field( $record, $fieldName[, $subfieldName [, $n ] ] );
513 Deletes the given field.
515 If $n is passed, only the Nth field will be deleted. $n = 1
516 will delete the first repeatable field, $n = 3 will delete the third.
522 my $record = $params->{record};
523 my $fieldName = $params->{field};
524 my $subfieldName = $params->{subfield};
525 my $field_numbers = $params->{field_numbers} // [];
527 if ( !defined $subfieldName or $subfieldName eq '' ) {
528 _delete_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
530 _delete_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
536 my $record = $params->{record};
537 my $fieldName = $params->{field};
538 my $field_numbers = $params->{field_numbers} // [];
540 my @fields = $record->field( $fieldName );
542 if ( @$field_numbers ) {
543 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
545 foreach my $field ( @fields ) {
546 $record->delete_field( $field );
550 sub _delete_subfield {
552 my $record = $params->{record};
553 my $fieldName = $params->{field};
554 my $subfieldName = $params->{subfield};
555 my $field_numbers = $params->{field_numbers} // [];
557 my @fields = $record->field( $fieldName );
559 if ( @$field_numbers ) {
560 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
563 foreach my $field ( @fields ) {
564 $field->delete_subfield( code => $subfieldName );
565 $record->delete_field( $field ) unless $field->subfields();
570 sub _copy_move_field {
572 my $record = $params->{record};
573 my $fromFieldName = $params->{from_field};
574 my $toFieldName = $params->{to_field};
575 my $regex = $params->{regex};
576 my $field_numbers = $params->{field_numbers} // [];
577 my $action = $params->{action} || 'copy';
579 my @from_fields = $record->field( $fromFieldName );
580 if ( @$field_numbers ) {
581 @from_fields = map { $_ <= @from_fields ? $from_fields[ $_ - 1 ] : () } @$field_numbers;
585 for my $from_field ( @from_fields ) {
586 my $new_field = $from_field->clone;
587 $new_field->{_tag} = $toFieldName; # Should be replaced by set_tag, introduced by MARC::Field 2.0.4
588 if ( $regex and $regex->{search} ) {
589 for my $subfield ( $new_field->subfields ) {
590 my $value = $subfield->[1];
591 ( $value ) = _modify_values({ values => [ $value ], regex => $regex });
592 $new_field->update( $subfield->[0], $value );
595 if ( $action eq 'move' ) {
596 $record->delete_field( $from_field )
598 elsif ( $action eq 'replace' ) {
599 my @to_fields = $record->field( $toFieldName );
601 $record->delete_field( $to_fields[0] );
604 push @new_fields, $new_field;
606 $record->append_fields( @new_fields );
609 sub _copy_move_subfield {
611 my $record = $params->{record};
612 my $fromFieldName = $params->{from_field};
613 my $fromSubfieldName = $params->{from_subfield};
614 my $toFieldName = $params->{to_field};
615 my $toSubfieldName = $params->{to_subfield};
616 my $regex = $params->{regex};
617 my $field_numbers = $params->{field_numbers} // [];
618 my $action = $params->{action} || 'copy';
620 my @values = read_field({ record => $record, field => $fromFieldName, subfield => $fromSubfieldName });
621 if ( @$field_numbers ) {
622 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
624 _modify_values({ values => \@values, regex => $regex });
625 my $dont_erase = $action eq 'copy' ? 1 : 0;
626 _update_subfield({ record => $record, field => $toFieldName, subfield => $toSubfieldName, values => \@values, dont_erase => $dont_erase });
628 # And delete if it's a move
629 if ( $action eq 'move' ) {
632 field => $fromFieldName,
633 subfield => $fromSubfieldName,
634 field_numbers => $field_numbers,
641 my $values = $params->{values};
642 my $regex = $params->{regex};
644 if ( $regex and $regex->{search} ) {
645 $regex->{modifiers} //= q||;
646 my @available_modifiers = qw( i g );
648 for my $modifier ( split //, $regex->{modifiers} ) {
649 $modifiers .= $modifier
650 if grep {/$modifier/} @available_modifiers;
652 foreach my $value ( @$values ) {
653 if ( $modifiers =~ m/^(ig|gi)$/ ) {
654 $value =~ s/$regex->{search}/$regex->{replace}/ig;
656 elsif ( $modifiers eq 'i' ) {
657 $value =~ s/$regex->{search}/$regex->{replace}/i;
659 elsif ( $modifiers eq 'g' ) {
660 $value =~ s/$regex->{search}/$regex->{replace}/g;
663 $value =~ s/$regex->{search}/$regex->{replace}/;