1 package Koha::SimpleMARC;
3 # Copyright 2009 Kyle M. Hall <kyle.m.hall@gmail.com>
11 our @ISA = qw(Exporter);
12 our %EXPORT_TAGS = ( 'all' => [ qw(
16 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
28 our $VERSION = '0.01';
34 SimpleMARC - Perl module for making simple MARC record alterations.
42 SimpleMARC is designed to make writing scripts
43 to modify MARC records simple and easy.
45 Every function in the modules requires a
46 MARC::Record object as its first parameter.
50 Kyle Hall <lt>kyle.m.hall@gmail.com<gt>
52 =head1 COPYRIGHT AND LICENSE
54 Copyright (C) 2009 by Kyle Hall
56 This library is free software; you can redistribute it and/or modify
57 it under the same terms as Perl itself, either Perl version 5.8.7 or,
58 at your option, any later version of Perl 5 you may have available.
64 copy_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex[, $n ] ] );
66 Copies a value from one field to another. If a regular expression ( $regex ) is supplied,
67 the value will be transformed by the given regex before being copied into the new field.
68 Example: $regex = { search => 'Old Text', replace => 'Replacement Text', modifiers => 'g' };
70 If $n is passed, copy_field will only copy the Nth field of the list of fields.
71 E.g. $n = 1 will only use the first field's value, $n = 2 will use only the 2nd field's value.
77 my $record = $params->{record};
78 my $fromFieldName = $params->{from_field};
79 my $fromSubfieldName = $params->{from_subfield};
80 my $toFieldName = $params->{to_field};
81 my $toSubfieldName = $params->{to_subfield};
82 my $regex = $params->{regex};
83 my $field_numbers = $params->{field_numbers} // [];
85 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
88 if ( not $fromSubfieldName or $fromSubfieldName eq ''
89 or not $toSubfieldName or $toSubfieldName eq ''
93 from_field => $fromFieldName,
94 to_field => $toFieldName,
96 field_numbers => $field_numbers,
101 from_field => $fromFieldName,
102 from_subfield => $fromSubfieldName,
103 to_field => $toFieldName,
104 to_subfield => $toSubfieldName,
106 field_numbers => $field_numbers,
114 my $record = $params->{record};
115 my $fromFieldName = $params->{from_field};
116 my $toFieldName = $params->{to_field};
117 my $regex = $params->{regex};
118 my $field_numbers = $params->{field_numbers} // [];
122 from_field => $fromFieldName,
123 to_field => $toFieldName,
125 field_numbers => $field_numbers,
132 my $record = $params->{record};
133 my $fromFieldName = $params->{from_field};
134 my $fromSubfieldName = $params->{from_subfield};
135 my $toFieldName = $params->{to_field};
136 my $toSubfieldName = $params->{to_subfield};
137 my $regex = $params->{regex};
138 my $field_numbers = $params->{field_numbers} // [];
140 _copy_move_subfield({
142 from_field => $fromFieldName,
143 from_subfield => $fromSubfieldName,
144 to_field => $toFieldName,
145 to_subfield => $toSubfieldName,
147 field_numbers => $field_numbers,
154 my $record = $params->{record};
155 my $fieldName = $params->{field};
156 my $subfieldName = $params->{subfield};
157 my @values = @{ $params->{values} };
158 my $field_numbers = $params->{field_numbers} // [];
160 if ( ! ( $record && $fieldName ) ) { return; }
162 if ( not $subfieldName or $subfieldName eq '' ) {
163 # FIXME I'm not sure the actual implementation is correct.
164 die "This action is not implemented yet";
165 #_update_field({ record => $record, field => $fieldName, values => \@values });
167 _update_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, values => \@values, field_numbers => $field_numbers });
173 my $record = $params->{record};
174 my $fieldName = $params->{field};
175 my @values = @{ $params->{values} };
178 if ( my @fields = $record->field( $fieldName ) ) {
179 @values = ($values[0]) x scalar( @fields )
181 foreach my $field ( @fields ) {
182 $field->update( $values[$i++] );
185 ## Field does not exists, create it
186 if ( $fieldName < 10 ) {
187 foreach my $value ( @values ) {
188 my $field = MARC::Field->new( $fieldName, $value );
189 $record->append_fields( $field );
192 warn "Invalid operation, trying to add a new field without subfield";
197 sub _update_subfield {
199 my $record = $params->{record};
200 my $fieldName = $params->{field};
201 my $subfieldName = $params->{subfield};
202 my @values = @{ $params->{values} };
203 my $dont_erase = $params->{dont_erase};
204 my $field_numbers = $params->{field_numbers} // [];
207 my @fields = $record->field( $fieldName );
209 if ( @$field_numbers ) {
210 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
214 unless ( $dont_erase ) {
215 @values = ($values[0]) x scalar( @fields )
217 foreach my $field ( @fields ) {
218 $field->update( "$subfieldName" => $values[$i++] );
221 if ( $i <= scalar ( @values ) - 1 ) {
222 foreach my $field ( @fields ) {
223 foreach my $j ( $i .. scalar( @values ) - 1) {
224 $field->add_subfields( "$subfieldName" => $values[$j] );
229 ## Field does not exist, create it.
230 foreach my $value ( @values ) {
231 my $field = MARC::Field->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
232 $record->append_fields( $field );
239 my @values = read_field( $record, $fieldName[, $subfieldName, [, $n ] ] );
241 Returns an array of field values for the given field and subfield
243 If $n is given, it will return only the $nth value of the array.
244 E.g. If $n = 1, it return the 1st value, if $n = 3, it will return the 3rd value.
250 my $record = $params->{record};
251 my $fieldName = $params->{field};
252 my $subfieldName = $params->{subfield};
253 my $field_numbers = $params->{field_numbers} // [];
255 if ( not $subfieldName or $subfieldName eq '' ) {
256 _read_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
258 _read_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
264 my $record = $params->{record};
265 my $fieldName = $params->{field};
266 my $field_numbers = $params->{field_numbers} // [];
268 my @fields = $record->field( $fieldName );
270 return unless @fields;
272 return map { $_->data() } @fields
276 if ( @$field_numbers ) {
277 for my $field_number ( @$field_numbers ) {
278 if ( $field_number <= scalar( @fields ) ) {
279 for my $sf ( $fields[$field_number - 1]->subfields ) {
280 push @values, $sf->[1];
285 foreach my $field ( @fields ) {
286 for my $sf ( $field->subfields ) {
287 push @values, $sf->[1];
297 my $record = $params->{record};
298 my $fieldName = $params->{field};
299 my $subfieldName = $params->{subfield};
300 my $field_numbers = $params->{field_numbers} // [];
302 my @fields = $record->field( $fieldName );
304 return unless @fields;
307 foreach my $field ( @fields ) {
308 my @sf = $field->subfield( $subfieldName );
309 push( @values, @sf );
312 if ( @values and @$field_numbers ) {
313 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
321 @field_numbers = field_exists( $record, $fieldName[, $subfieldName ]);
323 Returns the field numbers or an empty array.
329 my $record = $params->{record};
330 my $fieldName = $params->{field};
331 my $subfieldName = $params->{subfield};
333 if ( ! $record ) { return; }
335 my @field_numbers = ();
336 my $current_field_number = 1;
337 for my $field ( $record->field( $fieldName ) ) {
338 if ( $subfieldName ) {
339 push @field_numbers, $current_field_number
340 if $field->subfield( $subfieldName );
342 push @field_numbers, $current_field_number;
344 $current_field_number++;
347 return \@field_numbers;
352 $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
354 Returns true if the field equals the given value, false otherwise.
356 If a regular expression ( $regex ) is supplied, the value will be compared using
357 the given regex. Example: $regex = 'sought_text'
363 my $record = $params->{record};
364 my $value = $params->{value};
365 my $fieldName = $params->{field};
366 my $subfieldName = $params->{subfield};
367 my $is_regex = $params->{is_regex};
369 if ( ! $record ) { return; }
371 my @field_numbers = ();
372 my $current_field_number = 1;
373 FIELDS: for my $field ( $record->field( $fieldName ) ) {
374 my @subfield_values = $subfieldName
375 ? $field->subfield( $subfieldName )
376 : map { $_->[1] } $field->subfields;
378 SUBFIELDS: for my $subfield_value ( @subfield_values ) {
381 $is_regex and $subfield_value =~ m/$value/
383 $subfield_value eq $value
386 push @field_numbers, $current_field_number;
390 $current_field_number++;
393 return \@field_numbers;
398 move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex [, $n ] ] );
400 Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
401 the value will be transformed by the given regex before being moved into the new field.
402 Example: $regex = 's/Old Text/Replacement Text/'
404 If $n is passed, only the Nth field will be moved. $n = 1
405 will move the first repeatable field, $n = 3 will move the third.
411 my $record = $params->{record};
412 my $fromFieldName = $params->{from_field};
413 my $fromSubfieldName = $params->{from_subfield};
414 my $toFieldName = $params->{to_field};
415 my $toSubfieldName = $params->{to_subfield};
416 my $regex = $params->{regex};
417 my $field_numbers = $params->{field_numbers} // [];
419 if ( not $fromSubfieldName or $fromSubfieldName eq ''
420 or not $toSubfieldName or $toSubfieldName eq ''
424 from_field => $fromFieldName,
425 to_field => $toFieldName,
427 field_numbers => $field_numbers,
432 from_field => $fromFieldName,
433 from_subfield => $fromSubfieldName,
434 to_field => $toFieldName,
435 to_subfield => $toSubfieldName,
437 field_numbers => $field_numbers,
444 my $record = $params->{record};
445 my $fromFieldName = $params->{from_field};
446 my $toFieldName = $params->{to_field};
447 my $regex = $params->{regex};
448 my $field_numbers = $params->{field_numbers} // [];
452 from_field => $fromFieldName,
453 to_field => $toFieldName,
455 field_numbers => $field_numbers,
462 my $record = $params->{record};
463 my $fromFieldName = $params->{from_field};
464 my $fromSubfieldName = $params->{from_subfield};
465 my $toFieldName = $params->{to_field};
466 my $toSubfieldName = $params->{to_subfield};
467 my $regex = $params->{regex};
468 my $field_numbers = $params->{field_numbers} // [];
470 _copy_move_subfield({
472 from_field => $fromFieldName,
473 from_subfield => $fromSubfieldName,
474 to_field => $toFieldName,
475 to_subfield => $toSubfieldName,
477 field_numbers => $field_numbers,
484 _delete_field( $record, $fieldName[, $subfieldName [, $n ] ] );
486 Deletes the given field.
488 If $n is passed, only the Nth field will be deleted. $n = 1
489 will delete the first repeatable field, $n = 3 will delete the third.
495 my $record = $params->{record};
496 my $fieldName = $params->{field};
497 my $subfieldName = $params->{subfield};
498 my $field_numbers = $params->{field_numbers} // [];
500 if ( not $subfieldName or $subfieldName eq '' ) {
501 _delete_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
503 _delete_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
509 my $record = $params->{record};
510 my $fieldName = $params->{field};
511 my $field_numbers = $params->{field_numbers} // [];
513 my @fields = $record->field( $fieldName );
515 if ( @$field_numbers ) {
516 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
518 foreach my $field ( @fields ) {
519 $record->delete_field( $field );
523 sub _delete_subfield {
525 my $record = $params->{record};
526 my $fieldName = $params->{field};
527 my $subfieldName = $params->{subfield};
528 my $field_numbers = $params->{field_numbers} // [];
530 my @fields = $record->field( $fieldName );
532 if ( @$field_numbers ) {
533 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
536 foreach my $field ( @fields ) {
537 $field->delete_subfield( code => $subfieldName );
542 sub _copy_move_field {
544 my $record = $params->{record};
545 my $fromFieldName = $params->{from_field};
546 my $toFieldName = $params->{to_field};
547 my $regex = $params->{regex};
548 my $field_numbers = $params->{field_numbers} // [];
549 my $action = $params->{action} || 'copy';
551 my @fields = $record->field( $fromFieldName );
552 if ( @$field_numbers ) {
553 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
556 for my $field ( @fields ) {
557 my $new_field = $field->clone;
558 $new_field->{_tag} = $toFieldName; # Should be replaced by set_tag, introduced by MARC::Field 2.0.4
559 if ( $regex and $regex->{search} ) {
560 for my $subfield ( $new_field->subfields ) {
561 my $value = $subfield->[1];
562 ( $value ) = _modify_values({ values => [ $value ], regex => $regex });
563 $new_field->update( $subfield->[0], $value );
566 $record->append_fields( $new_field );
567 $record->delete_field( $field )
568 if $action eq 'move';
572 sub _copy_move_subfield {
574 my $record = $params->{record};
575 my $fromFieldName = $params->{from_field};
576 my $fromSubfieldName = $params->{from_subfield};
577 my $toFieldName = $params->{to_field};
578 my $toSubfieldName = $params->{to_subfield};
579 my $regex = $params->{regex};
580 my $field_numbers = $params->{field_numbers} // [];
581 my $action = $params->{action} || 'copy';
583 my @values = read_field({ record => $record, field => $fromFieldName, subfield => $fromSubfieldName });
584 if ( @$field_numbers ) {
585 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
587 _modify_values({ values => \@values, regex => $regex });
588 my $dont_erase = $action eq 'copy' ? 1 : 0;
589 _update_subfield({ record => $record, field => $toFieldName, subfield => $toSubfieldName, values => \@values, dont_erase => $dont_erase });
591 # And delete if it's a move
592 if ( $action eq 'move' ) {
595 field => $fromFieldName,
596 subfield => $fromSubfieldName,
597 field_numbers => $field_numbers,
604 my $values = $params->{values};
605 my $regex = $params->{regex};
607 if ( $regex and $regex->{search} ) {
608 $regex->{modifiers} //= q||;
609 my @available_modifiers = qw( i g );
611 for my $modifier ( split //, $regex->{modifiers} ) {
612 $modifiers .= $modifier
613 if grep {/$modifier/} @available_modifiers;
615 foreach my $value ( @$values ) {
616 if ( $modifiers =~ m/^(ig|gi)$/ ) {
617 $value =~ s/$regex->{search}/$regex->{replace}/ig;
619 elsif ( $modifiers eq 'i' ) {
620 $value =~ s/$regex->{search}/$regex->{replace}/i;
622 elsif ( $modifiers eq 'g' ) {
623 $value =~ s/$regex->{search}/$regex->{replace}/g;
626 $value =~ s/$regex->{search}/$regex->{replace}/;