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'} } );
22 copy_and_replace_field
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
89 or $fromSubfieldName eq ''
90 or not $toSubfieldName
91 or $toSubfieldName eq '' ) {
94 from_field
=> $fromFieldName,
95 to_field
=> $toFieldName,
97 field_numbers
=> $field_numbers,
104 from_field
=> $fromFieldName,
105 from_subfield
=> $fromSubfieldName,
106 to_field
=> $toFieldName,
107 to_subfield
=> $toSubfieldName,
109 field_numbers
=> $field_numbers,
116 sub copy_and_replace_field
{
118 my $record = $params->{record
};
119 my $fromFieldName = $params->{from_field
};
120 my $fromSubfieldName = $params->{from_subfield
};
121 my $toFieldName = $params->{to_field
};
122 my $toSubfieldName = $params->{to_subfield
};
123 my $regex = $params->{regex
};
124 my $field_numbers = $params->{field_numbers
} // [];
126 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
129 if ( not $fromSubfieldName or $fromSubfieldName eq ''
130 or not $toSubfieldName or $toSubfieldName eq ''
134 from_field
=> $fromFieldName,
135 to_field
=> $toFieldName,
137 field_numbers
=> $field_numbers,
144 from_field
=> $fromFieldName,
145 from_subfield
=> $fromSubfieldName,
146 to_field
=> $toFieldName,
147 to_subfield
=> $toSubfieldName,
149 field_numbers
=> $field_numbers,
158 my $record = $params->{record
};
159 my $fieldName = $params->{field
};
160 my $subfieldName = $params->{subfield
};
161 my @values = @
{ $params->{values} };
162 my $field_numbers = $params->{field_numbers
} // [];
164 if ( ! ( $record && $fieldName ) ) { return; }
166 if ( not $subfieldName or $subfieldName eq '' ) {
167 # FIXME I'm not sure the actual implementation is correct.
168 die "This action is not implemented yet";
169 #_update_field({ record => $record, field => $fieldName, values => \@values });
171 _update_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, values => \
@values, field_numbers
=> $field_numbers });
177 my $record = $params->{record
};
178 my $fieldName = $params->{field
};
179 my @values = @
{ $params->{values} };
182 if ( my @fields = $record->field( $fieldName ) ) {
183 @values = ($values[0]) x
scalar( @fields )
185 foreach my $field ( @fields ) {
186 $field->update( $values[$i++] );
189 ## Field does not exists, create it
190 if ( $fieldName < 10 ) {
191 foreach my $value ( @values ) {
192 my $field = MARC
::Field
->new( $fieldName, $value );
193 $record->append_fields( $field );
196 warn "Invalid operation, trying to add a new field without subfield";
201 sub _update_subfield
{
203 my $record = $params->{record
};
204 my $fieldName = $params->{field
};
205 my $subfieldName = $params->{subfield
};
206 my @values = @
{ $params->{values} };
207 my $dont_erase = $params->{dont_erase
};
208 my $field_numbers = $params->{field_numbers
} // [];
211 my @fields = $record->field( $fieldName );
213 if ( @
$field_numbers ) {
214 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
218 unless ( $dont_erase ) {
219 @values = ($values[0]) x
scalar( @fields )
221 foreach my $field ( @fields ) {
222 $field->update( "$subfieldName" => $values[$i++] );
225 if ( $i <= scalar ( @values ) - 1 ) {
226 foreach my $field ( @fields ) {
227 foreach my $j ( $i .. scalar( @values ) - 1) {
228 $field->add_subfields( "$subfieldName" => $values[$j] );
233 ## Field does not exist, create it.
234 foreach my $value ( @values ) {
235 my $field = MARC
::Field
->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
236 $record->append_fields( $field );
243 my @values = read_field( $record, $fieldName[, $subfieldName, [, $n ] ] );
245 Returns an array of field values for the given field and subfield
247 If $n is given, it will return only the $nth value of the array.
248 E.g. If $n = 1, it return the 1st value, if $n = 3, it will return the 3rd value.
254 my $record = $params->{record
};
255 my $fieldName = $params->{field
};
256 my $subfieldName = $params->{subfield
};
257 my $field_numbers = $params->{field_numbers
} // [];
259 if ( not $subfieldName or $subfieldName eq '' ) {
260 _read_field
({ record
=> $record, field
=> $fieldName, field_numbers
=> $field_numbers });
262 _read_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, field_numbers
=> $field_numbers });
268 my $record = $params->{record
};
269 my $fieldName = $params->{field
};
270 my $field_numbers = $params->{field_numbers
} // [];
272 my @fields = $record->field( $fieldName );
274 return unless @fields;
276 return map { $_->data() } @fields
280 if ( @
$field_numbers ) {
281 for my $field_number ( @
$field_numbers ) {
282 if ( $field_number <= scalar( @fields ) ) {
283 for my $sf ( $fields[$field_number - 1]->subfields ) {
284 push @values, $sf->[1];
289 foreach my $field ( @fields ) {
290 for my $sf ( $field->subfields ) {
291 push @values, $sf->[1];
301 my $record = $params->{record
};
302 my $fieldName = $params->{field
};
303 my $subfieldName = $params->{subfield
};
304 my $field_numbers = $params->{field_numbers
} // [];
306 my @fields = $record->field( $fieldName );
308 return unless @fields;
311 foreach my $field ( @fields ) {
312 my @sf = $field->subfield( $subfieldName );
313 push( @values, @sf );
316 if ( @values and @
$field_numbers ) {
317 @values = map { $_ <= @values ?
$values[ $_ - 1 ] : () } @
$field_numbers;
325 @field_numbers = field_exists( $record, $fieldName[, $subfieldName ]);
327 Returns the field numbers or an empty array.
333 my $record = $params->{record
};
334 my $fieldName = $params->{field
};
335 my $subfieldName = $params->{subfield
};
337 if ( ! $record ) { return; }
339 my @field_numbers = ();
340 my $current_field_number = 1;
341 for my $field ( $record->field( $fieldName ) ) {
342 if ( $subfieldName ) {
343 push @field_numbers, $current_field_number
344 if $field->subfield( $subfieldName );
346 push @field_numbers, $current_field_number;
348 $current_field_number++;
351 return \
@field_numbers;
356 $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
358 Returns true if the field equals the given value, false otherwise.
360 If a regular expression ( $regex ) is supplied, the value will be compared using
361 the given regex. Example: $regex = 'sought_text'
367 my $record = $params->{record
};
368 my $value = $params->{value
};
369 my $fieldName = $params->{field
};
370 my $subfieldName = $params->{subfield
};
371 my $is_regex = $params->{is_regex
};
373 if ( ! $record ) { return; }
375 my @field_numbers = ();
376 my $current_field_number = 1;
377 FIELDS
: for my $field ( $record->field( $fieldName ) ) {
378 my @subfield_values = $subfieldName
379 ?
$field->subfield( $subfieldName )
380 : map { $_->[1] } $field->subfields;
382 SUBFIELDS
: for my $subfield_value ( @subfield_values ) {
385 $is_regex and $subfield_value =~ m/$value/
387 $subfield_value eq $value
390 push @field_numbers, $current_field_number;
394 $current_field_number++;
397 return \
@field_numbers;
402 move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex [, $n ] ] );
404 Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
405 the value will be transformed by the given regex before being moved into the new field.
406 Example: $regex = 's/Old Text/Replacement Text/'
408 If $n is passed, only the Nth field will be moved. $n = 1
409 will move the first repeatable field, $n = 3 will move the third.
415 my $record = $params->{record
};
416 my $fromFieldName = $params->{from_field
};
417 my $fromSubfieldName = $params->{from_subfield
};
418 my $toFieldName = $params->{to_field
};
419 my $toSubfieldName = $params->{to_subfield
};
420 my $regex = $params->{regex
};
421 my $field_numbers = $params->{field_numbers
} // [];
423 if ( not $fromSubfieldName
424 or $fromSubfieldName eq ''
425 or not $toSubfieldName
426 or $toSubfieldName eq '' ) {
429 from_field
=> $fromFieldName,
430 to_field
=> $toFieldName,
432 field_numbers
=> $field_numbers,
439 from_field
=> $fromFieldName,
440 from_subfield
=> $fromSubfieldName,
441 to_field
=> $toFieldName,
442 to_subfield
=> $toSubfieldName,
444 field_numbers
=> $field_numbers,
453 _delete_field( $record, $fieldName[, $subfieldName [, $n ] ] );
455 Deletes the given field.
457 If $n is passed, only the Nth field will be deleted. $n = 1
458 will delete the first repeatable field, $n = 3 will delete the third.
464 my $record = $params->{record
};
465 my $fieldName = $params->{field
};
466 my $subfieldName = $params->{subfield
};
467 my $field_numbers = $params->{field_numbers
} // [];
469 if ( not $subfieldName or $subfieldName eq '' ) {
470 _delete_field
({ record
=> $record, field
=> $fieldName, field_numbers
=> $field_numbers });
472 _delete_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, field_numbers
=> $field_numbers });
478 my $record = $params->{record
};
479 my $fieldName = $params->{field
};
480 my $field_numbers = $params->{field_numbers
} // [];
482 my @fields = $record->field( $fieldName );
484 if ( @
$field_numbers ) {
485 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
487 foreach my $field ( @fields ) {
488 $record->delete_field( $field );
492 sub _delete_subfield
{
494 my $record = $params->{record
};
495 my $fieldName = $params->{field
};
496 my $subfieldName = $params->{subfield
};
497 my $field_numbers = $params->{field_numbers
} // [];
499 my @fields = $record->field( $fieldName );
501 if ( @
$field_numbers ) {
502 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
505 foreach my $field ( @fields ) {
506 $field->delete_subfield( code
=> $subfieldName );
511 sub _copy_move_field
{
513 my $record = $params->{record
};
514 my $fromFieldName = $params->{from_field
};
515 my $toFieldName = $params->{to_field
};
516 my $regex = $params->{regex
};
517 my $field_numbers = $params->{field_numbers
} // [];
518 my $action = $params->{action
} || 'copy';
520 my @from_fields = $record->field( $fromFieldName );
521 if ( @
$field_numbers ) {
522 @from_fields = map { $_ <= @from_fields ?
$from_fields[ $_ - 1 ] : () } @
$field_numbers;
526 for my $from_field ( @from_fields ) {
527 my $new_field = $from_field->clone;
528 $new_field->{_tag
} = $toFieldName; # Should be replaced by set_tag, introduced by MARC::Field 2.0.4
529 if ( $regex and $regex->{search
} ) {
530 for my $subfield ( $new_field->subfields ) {
531 my $value = $subfield->[1];
532 ( $value ) = _modify_values
({ values => [ $value ], regex
=> $regex });
533 $new_field->update( $subfield->[0], $value );
536 if ( $action eq 'move' ) {
537 $record->delete_field( $from_field )
539 elsif ( $action eq 'replace' ) {
540 my @to_fields = $record->field( $toFieldName );
542 $record->delete_field( $to_fields[0] );
545 push @new_fields, $new_field;
547 $record->append_fields( @new_fields );
550 sub _copy_move_subfield
{
552 my $record = $params->{record
};
553 my $fromFieldName = $params->{from_field
};
554 my $fromSubfieldName = $params->{from_subfield
};
555 my $toFieldName = $params->{to_field
};
556 my $toSubfieldName = $params->{to_subfield
};
557 my $regex = $params->{regex
};
558 my $field_numbers = $params->{field_numbers
} // [];
559 my $action = $params->{action
} || 'copy';
561 my @values = read_field
({ record
=> $record, field
=> $fromFieldName, subfield
=> $fromSubfieldName });
562 if ( @
$field_numbers ) {
563 @values = map { $_ <= @values ?
$values[ $_ - 1 ] : () } @
$field_numbers;
565 _modify_values
({ values => \
@values, regex
=> $regex });
566 my $dont_erase = $action eq 'copy' ?
1 : 0;
567 _update_subfield
({ record
=> $record, field
=> $toFieldName, subfield
=> $toSubfieldName, values => \
@values, dont_erase
=> $dont_erase });
569 # And delete if it's a move
570 if ( $action eq 'move' ) {
573 field
=> $fromFieldName,
574 subfield
=> $fromSubfieldName,
575 field_numbers
=> $field_numbers,
582 my $values = $params->{values};
583 my $regex = $params->{regex
};
585 if ( $regex and $regex->{search
} ) {
586 $regex->{modifiers
} //= q
||;
587 my @available_modifiers = qw( i g );
589 for my $modifier ( split //, $regex->{modifiers
} ) {
590 $modifiers .= $modifier
591 if grep {/$modifier/} @available_modifiers;
593 foreach my $value ( @
$values ) {
594 if ( $modifiers =~ m/^(ig|gi)$/ ) {
595 $value =~ s/$regex->{search}/$regex->{replace}/ig;
597 elsif ( $modifiers eq 'i' ) {
598 $value =~ s/$regex->{search}/$regex->{replace}/i;
600 elsif ( $modifiers eq 'g' ) {
601 $value =~ s/$regex->{search}/$regex->{replace}/g;
604 $value =~ s/$regex->{search}/$regex->{replace}/;