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
49 SimpleMARC - Perl module for making simple MARC record alterations.
57 SimpleMARC is designed to make writing scripts
58 to modify MARC records simple and easy.
60 Every function in the modules requires a
61 MARC::Record object as its first parameter.
65 Kyle Hall <lt>kyle.m.hall@gmail.com<gt>
67 =head1 COPYRIGHT AND LICENSE
69 Copyright (C) 2009 by Kyle Hall
71 This library is free software; you can redistribute it and/or modify
72 it under the same terms as Perl itself, either Perl version 5.8.7 or,
73 at your option, any later version of Perl 5 you may have available.
79 copy_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex[, $n ] ] );
81 Copies a value from one field to another. If a regular expression ( $regex ) is supplied,
82 the value will be transformed by the given regex before being copied into the new field.
83 Example: $regex = { search => 'Old Text', replace => 'Replacement Text', modifiers => 'g' };
85 If $n is passed, copy_field will only copy the Nth field of the list of fields.
86 E.g. $n = 1 will only use the first field's value, $n = 2 will use only the 2nd field's value.
92 my $record = $params->{record
};
93 my $fromFieldName = $params->{from_field
};
94 my $fromSubfieldName = $params->{from_subfield
};
95 my $toFieldName = $params->{to_field
};
96 my $toSubfieldName = $params->{to_subfield
};
97 my $regex = $params->{regex
};
98 my $field_numbers = $params->{field_numbers
} // [];
100 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
103 if ( not $fromSubfieldName
104 or $fromSubfieldName eq ''
105 or not $toSubfieldName
106 or $toSubfieldName eq '' ) {
109 from_field
=> $fromFieldName,
110 to_field
=> $toFieldName,
112 field_numbers
=> $field_numbers,
119 from_field
=> $fromFieldName,
120 from_subfield
=> $fromSubfieldName,
121 to_field
=> $toFieldName,
122 to_subfield
=> $toSubfieldName,
124 field_numbers
=> $field_numbers,
131 sub copy_and_replace_field
{
133 my $record = $params->{record
};
134 my $fromFieldName = $params->{from_field
};
135 my $fromSubfieldName = $params->{from_subfield
};
136 my $toFieldName = $params->{to_field
};
137 my $toSubfieldName = $params->{to_subfield
};
138 my $regex = $params->{regex
};
139 my $field_numbers = $params->{field_numbers
} // [];
141 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
144 if ( not $fromSubfieldName or $fromSubfieldName eq ''
145 or not $toSubfieldName or $toSubfieldName eq ''
149 from_field
=> $fromFieldName,
150 to_field
=> $toFieldName,
152 field_numbers
=> $field_numbers,
159 from_field
=> $fromFieldName,
160 from_subfield
=> $fromSubfieldName,
161 to_field
=> $toFieldName,
162 to_subfield
=> $toSubfieldName,
164 field_numbers
=> $field_numbers,
173 my $record = $params->{record
};
174 my $fieldName = $params->{field
};
175 my $subfieldName = $params->{subfield
};
176 my @values = @
{ $params->{values} };
177 my $field_numbers = $params->{field_numbers
} // [];
179 if ( ! ( $record && $fieldName ) ) { return; }
181 if ( not defined $subfieldName or $subfieldName eq '' ) {
182 # FIXME I'm not sure the actual implementation is correct.
183 die "This action is not implemented yet";
184 #_update_field({ record => $record, field => $fieldName, values => \@values });
186 _update_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, values => \
@values, field_numbers
=> $field_numbers });
195 subfield => $subfieldName,
197 field_numbers => $field_numbers,
200 Adds a new field/subfield with supplied value(s).
201 This function always add a new field as opposed to 'update_field' which will
202 either update if field exists and add if it does not.
209 my $record = $params->{record
};
210 my $fieldName = $params->{field
};
211 my $subfieldName = $params->{subfield
};
212 my @values = @
{ $params->{values} };
213 my $field_numbers = $params->{field_numbers
} // [];
215 if ( ! ( $record && $fieldName ) ) { return; }
216 if ( $fieldName > 10 ) {
217 foreach my $value ( @values ) {
218 my $field = MARC
::Field
->new( $fieldName, '', '', "$subfieldName" => $value );
219 $record->append_fields( $field );
222 foreach my $value ( @values ) {
223 my $field = MARC
::Field
->new( $fieldName, $value );
224 $record->append_fields( $field );
231 my $record = $params->{record
};
232 my $fieldName = $params->{field
};
233 my @values = @
{ $params->{values} };
236 if ( my @fields = $record->field( $fieldName ) ) {
237 @values = ($values[0]) x
scalar( @fields )
239 foreach my $field ( @fields ) {
240 $field->update( $values[$i++] );
243 ## Field does not exists, create it
244 if ( $fieldName < 10 ) {
245 foreach my $value ( @values ) {
246 my $field = MARC
::Field
->new( $fieldName, $value );
247 $record->append_fields( $field );
250 warn "Invalid operation, trying to add a new field without subfield";
255 sub _update_subfield
{
257 my $record = $params->{record
};
258 my $fieldName = $params->{field
};
259 my $subfieldName = $params->{subfield
};
260 my @values = @
{ $params->{values} };
261 my $dont_erase = $params->{dont_erase
};
262 my $field_numbers = $params->{field_numbers
} // [];
265 my @fields = $record->field( $fieldName );
267 if ( @
$field_numbers ) {
268 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
272 unless ( $dont_erase ) {
273 @values = ($values[0]) x
scalar( @fields )
275 foreach my $field ( @fields ) {
276 $field->update( "$subfieldName" => $values[$i++] );
279 if ( $i <= scalar ( @values ) - 1 ) {
280 foreach my $field ( @fields ) {
281 foreach my $j ( $i .. scalar( @values ) - 1) {
282 $field->add_subfields( "$subfieldName" => $values[$j] );
287 ## Field does not exist, create it.
288 foreach my $value ( @values ) {
289 my $field = MARC
::Field
->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
290 $record->append_fields( $field );
297 my @values = read_field( $record, $fieldName[, $subfieldName, [, $n ] ] );
299 Returns an array of field values for the given field and subfield
301 If $n is given, it will return only the $nth value of the array.
302 E.g. If $n = 1, it return the 1st value, if $n = 3, it will return the 3rd value.
308 my $record = $params->{record
};
309 my $fieldName = $params->{field
};
310 my $subfieldName = $params->{subfield
};
311 my $field_numbers = $params->{field_numbers
} // [];
313 if ( not defined $subfieldName or $subfieldName eq '' ) {
314 _read_field
({ record
=> $record, field
=> $fieldName, field_numbers
=> $field_numbers });
316 _read_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, field_numbers
=> $field_numbers });
322 my $record = $params->{record
};
323 my $fieldName = $params->{field
};
324 my $field_numbers = $params->{field_numbers
} // [];
326 my @fields = $record->field( $fieldName );
328 return unless @fields;
330 return map { $_->data() } @fields
334 if ( @
$field_numbers ) {
335 for my $field_number ( @
$field_numbers ) {
336 if ( $field_number <= scalar( @fields ) ) {
337 for my $sf ( $fields[$field_number - 1]->subfields ) {
338 push @values, $sf->[1];
343 foreach my $field ( @fields ) {
344 for my $sf ( $field->subfields ) {
345 push @values, $sf->[1];
355 my $record = $params->{record
};
356 my $fieldName = $params->{field
};
357 my $subfieldName = $params->{subfield
};
358 my $field_numbers = $params->{field_numbers
} // [];
360 my @fields = $record->field( $fieldName );
362 return unless @fields;
365 foreach my $field ( @fields ) {
366 my @sf = $field->subfield( $subfieldName );
367 push( @values, @sf );
370 if ( @values and @
$field_numbers ) {
371 @values = map { $_ <= @values ?
$values[ $_ - 1 ] : () } @
$field_numbers;
379 @field_numbers = field_exists( $record, $fieldName[, $subfieldName ]);
381 Returns the field numbers or an empty array.
387 my $record = $params->{record
};
388 my $fieldName = $params->{field
};
389 my $subfieldName = $params->{subfield
};
391 if ( ! $record ) { return; }
393 my @field_numbers = ();
394 my $current_field_number = 1;
395 for my $field ( $record->field( $fieldName ) ) {
396 if ( $subfieldName ) {
397 push @field_numbers, $current_field_number
398 if $field->subfield( $subfieldName );
400 push @field_numbers, $current_field_number;
402 $current_field_number++;
405 return \
@field_numbers;
410 $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
412 Returns true if the field equals the given value, false otherwise.
414 If a regular expression ( $regex ) is supplied, the value will be compared using
415 the given regex. Example: $regex = 'sought_text'
421 my $record = $params->{record
};
422 my $value = $params->{value
};
423 my $fieldName = $params->{field
};
424 my $subfieldName = $params->{subfield
};
425 my $is_regex = $params->{is_regex
};
427 if ( ! $record ) { return; }
429 my @field_numbers = ();
430 my $current_field_number = 1;
431 FIELDS
: for my $field ( $record->field( $fieldName ) ) {
433 if ( $field->is_control_field ) {
434 push @subfield_values, $field->data;
438 ?
$field->subfield($subfieldName)
439 : map { $_->[1] } $field->subfields;
442 SUBFIELDS
: for my $subfield_value ( @subfield_values ) {
445 $is_regex and $subfield_value =~ m/$value/
447 $subfield_value eq $value
450 push @field_numbers, $current_field_number;
454 $current_field_number++;
457 return \
@field_numbers;
462 move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex [, $n ] ] );
464 Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
465 the value will be transformed by the given regex before being moved into the new field.
466 Example: $regex = 's/Old Text/Replacement Text/'
468 If $n is passed, only the Nth field will be moved. $n = 1
469 will move the first repeatable field, $n = 3 will move the third.
475 my $record = $params->{record
};
476 my $fromFieldName = $params->{from_field
};
477 my $fromSubfieldName = $params->{from_subfield
};
478 my $toFieldName = $params->{to_field
};
479 my $toSubfieldName = $params->{to_subfield
};
480 my $regex = $params->{regex
};
481 my $field_numbers = $params->{field_numbers
} // [];
483 if ( not $fromSubfieldName
484 or $fromSubfieldName eq ''
485 or not $toSubfieldName
486 or $toSubfieldName eq '' ) {
489 from_field
=> $fromFieldName,
490 to_field
=> $toFieldName,
492 field_numbers
=> $field_numbers,
499 from_field
=> $fromFieldName,
500 from_subfield
=> $fromSubfieldName,
501 to_field
=> $toFieldName,
502 to_subfield
=> $toSubfieldName,
504 field_numbers
=> $field_numbers,
513 _delete_field( $record, $fieldName[, $subfieldName [, $n ] ] );
515 Deletes the given field.
517 If $n is passed, only the Nth field will be deleted. $n = 1
518 will delete the first repeatable field, $n = 3 will delete the third.
524 my $record = $params->{record
};
525 my $fieldName = $params->{field
};
526 my $subfieldName = $params->{subfield
};
527 my $field_numbers = $params->{field_numbers
} // [];
529 if ( not $subfieldName or $subfieldName eq '' ) {
530 _delete_field
({ record
=> $record, field
=> $fieldName, field_numbers
=> $field_numbers });
532 _delete_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, field_numbers
=> $field_numbers });
538 my $record = $params->{record
};
539 my $fieldName = $params->{field
};
540 my $field_numbers = $params->{field_numbers
} // [];
542 my @fields = $record->field( $fieldName );
544 if ( @
$field_numbers ) {
545 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
547 foreach my $field ( @fields ) {
548 $record->delete_field( $field );
552 sub _delete_subfield
{
554 my $record = $params->{record
};
555 my $fieldName = $params->{field
};
556 my $subfieldName = $params->{subfield
};
557 my $field_numbers = $params->{field_numbers
} // [];
559 my @fields = $record->field( $fieldName );
561 if ( @
$field_numbers ) {
562 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
565 foreach my $field ( @fields ) {
566 $field->delete_subfield( code
=> $subfieldName );
571 sub _copy_move_field
{
573 my $record = $params->{record
};
574 my $fromFieldName = $params->{from_field
};
575 my $toFieldName = $params->{to_field
};
576 my $regex = $params->{regex
};
577 my $field_numbers = $params->{field_numbers
} // [];
578 my $action = $params->{action
} || 'copy';
580 my @from_fields = $record->field( $fromFieldName );
581 if ( @
$field_numbers ) {
582 @from_fields = map { $_ <= @from_fields ?
$from_fields[ $_ - 1 ] : () } @
$field_numbers;
586 for my $from_field ( @from_fields ) {
587 my $new_field = $from_field->clone;
588 $new_field->{_tag
} = $toFieldName; # Should be replaced by set_tag, introduced by MARC::Field 2.0.4
589 if ( $regex and $regex->{search
} ) {
590 for my $subfield ( $new_field->subfields ) {
591 my $value = $subfield->[1];
592 ( $value ) = _modify_values
({ values => [ $value ], regex
=> $regex });
593 $new_field->update( $subfield->[0], $value );
596 if ( $action eq 'move' ) {
597 $record->delete_field( $from_field )
599 elsif ( $action eq 'replace' ) {
600 my @to_fields = $record->field( $toFieldName );
602 $record->delete_field( $to_fields[0] );
605 push @new_fields, $new_field;
607 $record->append_fields( @new_fields );
610 sub _copy_move_subfield
{
612 my $record = $params->{record
};
613 my $fromFieldName = $params->{from_field
};
614 my $fromSubfieldName = $params->{from_subfield
};
615 my $toFieldName = $params->{to_field
};
616 my $toSubfieldName = $params->{to_subfield
};
617 my $regex = $params->{regex
};
618 my $field_numbers = $params->{field_numbers
} // [];
619 my $action = $params->{action
} || 'copy';
621 my @values = read_field
({ record
=> $record, field
=> $fromFieldName, subfield
=> $fromSubfieldName });
622 if ( @
$field_numbers ) {
623 @values = map { $_ <= @values ?
$values[ $_ - 1 ] : () } @
$field_numbers;
625 _modify_values
({ values => \
@values, regex
=> $regex });
626 my $dont_erase = $action eq 'copy' ?
1 : 0;
627 _update_subfield
({ record
=> $record, field
=> $toFieldName, subfield
=> $toSubfieldName, values => \
@values, dont_erase
=> $dont_erase });
629 # And delete if it's a move
630 if ( $action eq 'move' ) {
633 field
=> $fromFieldName,
634 subfield
=> $fromSubfieldName,
635 field_numbers
=> $field_numbers,
642 my $values = $params->{values};
643 my $regex = $params->{regex
};
645 if ( $regex and $regex->{search
} ) {
646 $regex->{modifiers
} //= q
||;
647 my @available_modifiers = qw( i g );
649 for my $modifier ( split //, $regex->{modifiers
} ) {
650 $modifiers .= $modifier
651 if grep {/$modifier/} @available_modifiers;
653 foreach my $value ( @
$values ) {
654 if ( $modifiers =~ m/^(ig|gi)$/ ) {
655 $value =~ s/$regex->{search}/$regex->{replace}/ig;
657 elsif ( $modifiers eq 'i' ) {
658 $value =~ s/$regex->{search}/$regex->{replace}/i;
660 elsif ( $modifiers eq 'g' ) {
661 $value =~ s/$regex->{search}/$regex->{replace}/g;
664 $value =~ s/$regex->{search}/$regex->{replace}/;