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'} } );
23 copy_and_replace_field
35 SimpleMARC - Perl module for making simple MARC record alterations.
43 SimpleMARC is designed to make writing scripts
44 to modify MARC records simple and easy.
46 Every function in the modules requires a
47 MARC::Record object as its first parameter.
51 Kyle Hall <lt>kyle.m.hall@gmail.com<gt>
53 =head1 COPYRIGHT AND LICENSE
55 Copyright (C) 2009 by Kyle Hall
57 This library is free software; you can redistribute it and/or modify
58 it under the same terms as Perl itself, either Perl version 5.8.7 or,
59 at your option, any later version of Perl 5 you may have available.
65 copy_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex[, $n ] ] );
67 Copies a value from one field to another. If a regular expression ( $regex ) is supplied,
68 the value will be transformed by the given regex before being copied into the new field.
69 Example: $regex = { search => 'Old Text', replace => 'Replacement Text', modifiers => 'g' };
71 If $n is passed, copy_field will only copy the Nth field of the list of fields.
72 E.g. $n = 1 will only use the first field's value, $n = 2 will use only the 2nd field's value.
78 my $record = $params->{record
};
79 my $fromFieldName = $params->{from_field
};
80 my $fromSubfieldName = $params->{from_subfield
};
81 my $toFieldName = $params->{to_field
};
82 my $toSubfieldName = $params->{to_subfield
};
83 my $regex = $params->{regex
};
84 my $field_numbers = $params->{field_numbers
} // [];
86 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
89 if ( not $fromSubfieldName
90 or $fromSubfieldName eq ''
91 or not $toSubfieldName
92 or $toSubfieldName eq '' ) {
95 from_field
=> $fromFieldName,
96 to_field
=> $toFieldName,
98 field_numbers
=> $field_numbers,
105 from_field
=> $fromFieldName,
106 from_subfield
=> $fromSubfieldName,
107 to_field
=> $toFieldName,
108 to_subfield
=> $toSubfieldName,
110 field_numbers
=> $field_numbers,
117 sub copy_and_replace_field
{
119 my $record = $params->{record
};
120 my $fromFieldName = $params->{from_field
};
121 my $fromSubfieldName = $params->{from_subfield
};
122 my $toFieldName = $params->{to_field
};
123 my $toSubfieldName = $params->{to_subfield
};
124 my $regex = $params->{regex
};
125 my $field_numbers = $params->{field_numbers
} // [];
127 if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
130 if ( not $fromSubfieldName or $fromSubfieldName eq ''
131 or not $toSubfieldName or $toSubfieldName eq ''
135 from_field
=> $fromFieldName,
136 to_field
=> $toFieldName,
138 field_numbers
=> $field_numbers,
145 from_field
=> $fromFieldName,
146 from_subfield
=> $fromSubfieldName,
147 to_field
=> $toFieldName,
148 to_subfield
=> $toSubfieldName,
150 field_numbers
=> $field_numbers,
159 my $record = $params->{record
};
160 my $fieldName = $params->{field
};
161 my $subfieldName = $params->{subfield
};
162 my @values = @
{ $params->{values} };
163 my $field_numbers = $params->{field_numbers
} // [];
165 if ( ! ( $record && $fieldName ) ) { return; }
167 if ( not $subfieldName or $subfieldName eq '' ) {
168 # FIXME I'm not sure the actual implementation is correct.
169 die "This action is not implemented yet";
170 #_update_field({ record => $record, field => $fieldName, values => \@values });
172 _update_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, values => \
@values, field_numbers
=> $field_numbers });
181 subfield => $subfieldName,
183 field_numbers => $field_numbers,
186 Adds a new field/subfield with supplied value(s).
187 This function always add a new field as opposed to 'update_field' which will
188 either update if field exists and add if it does not.
195 my $record = $params->{record
};
196 my $fieldName = $params->{field
};
197 my $subfieldName = $params->{subfield
};
198 my @values = @
{ $params->{values} };
199 my $field_numbers = $params->{field_numbers
} // [];
201 if ( ! ( $record && $fieldName ) ) { return; }
202 if ( $fieldName > 10 ) {
203 foreach my $value ( @values ) {
204 my $field = MARC
::Field
->new( $fieldName, '', '', "$subfieldName" => $value );
205 $record->append_fields( $field );
208 foreach my $value ( @values ) {
209 my $field = MARC
::Field
->new( $fieldName, $value );
210 $record->append_fields( $field );
217 my $record = $params->{record
};
218 my $fieldName = $params->{field
};
219 my @values = @
{ $params->{values} };
222 if ( my @fields = $record->field( $fieldName ) ) {
223 @values = ($values[0]) x
scalar( @fields )
225 foreach my $field ( @fields ) {
226 $field->update( $values[$i++] );
229 ## Field does not exists, create it
230 if ( $fieldName < 10 ) {
231 foreach my $value ( @values ) {
232 my $field = MARC
::Field
->new( $fieldName, $value );
233 $record->append_fields( $field );
236 warn "Invalid operation, trying to add a new field without subfield";
241 sub _update_subfield
{
243 my $record = $params->{record
};
244 my $fieldName = $params->{field
};
245 my $subfieldName = $params->{subfield
};
246 my @values = @
{ $params->{values} };
247 my $dont_erase = $params->{dont_erase
};
248 my $field_numbers = $params->{field_numbers
} // [];
251 my @fields = $record->field( $fieldName );
253 if ( @
$field_numbers ) {
254 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
258 unless ( $dont_erase ) {
259 @values = ($values[0]) x
scalar( @fields )
261 foreach my $field ( @fields ) {
262 $field->update( "$subfieldName" => $values[$i++] );
265 if ( $i <= scalar ( @values ) - 1 ) {
266 foreach my $field ( @fields ) {
267 foreach my $j ( $i .. scalar( @values ) - 1) {
268 $field->add_subfields( "$subfieldName" => $values[$j] );
273 ## Field does not exist, create it.
274 foreach my $value ( @values ) {
275 my $field = MARC
::Field
->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
276 $record->append_fields( $field );
283 my @values = read_field( $record, $fieldName[, $subfieldName, [, $n ] ] );
285 Returns an array of field values for the given field and subfield
287 If $n is given, it will return only the $nth value of the array.
288 E.g. If $n = 1, it return the 1st value, if $n = 3, it will return the 3rd value.
294 my $record = $params->{record
};
295 my $fieldName = $params->{field
};
296 my $subfieldName = $params->{subfield
};
297 my $field_numbers = $params->{field_numbers
} // [];
299 if ( not $subfieldName or $subfieldName eq '' ) {
300 _read_field
({ record
=> $record, field
=> $fieldName, field_numbers
=> $field_numbers });
302 _read_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, field_numbers
=> $field_numbers });
308 my $record = $params->{record
};
309 my $fieldName = $params->{field
};
310 my $field_numbers = $params->{field_numbers
} // [];
312 my @fields = $record->field( $fieldName );
314 return unless @fields;
316 return map { $_->data() } @fields
320 if ( @
$field_numbers ) {
321 for my $field_number ( @
$field_numbers ) {
322 if ( $field_number <= scalar( @fields ) ) {
323 for my $sf ( $fields[$field_number - 1]->subfields ) {
324 push @values, $sf->[1];
329 foreach my $field ( @fields ) {
330 for my $sf ( $field->subfields ) {
331 push @values, $sf->[1];
341 my $record = $params->{record
};
342 my $fieldName = $params->{field
};
343 my $subfieldName = $params->{subfield
};
344 my $field_numbers = $params->{field_numbers
} // [];
346 my @fields = $record->field( $fieldName );
348 return unless @fields;
351 foreach my $field ( @fields ) {
352 my @sf = $field->subfield( $subfieldName );
353 push( @values, @sf );
356 if ( @values and @
$field_numbers ) {
357 @values = map { $_ <= @values ?
$values[ $_ - 1 ] : () } @
$field_numbers;
365 @field_numbers = field_exists( $record, $fieldName[, $subfieldName ]);
367 Returns the field numbers or an empty array.
373 my $record = $params->{record
};
374 my $fieldName = $params->{field
};
375 my $subfieldName = $params->{subfield
};
377 if ( ! $record ) { return; }
379 my @field_numbers = ();
380 my $current_field_number = 1;
381 for my $field ( $record->field( $fieldName ) ) {
382 if ( $subfieldName ) {
383 push @field_numbers, $current_field_number
384 if $field->subfield( $subfieldName );
386 push @field_numbers, $current_field_number;
388 $current_field_number++;
391 return \
@field_numbers;
396 $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
398 Returns true if the field equals the given value, false otherwise.
400 If a regular expression ( $regex ) is supplied, the value will be compared using
401 the given regex. Example: $regex = 'sought_text'
407 my $record = $params->{record
};
408 my $value = $params->{value
};
409 my $fieldName = $params->{field
};
410 my $subfieldName = $params->{subfield
};
411 my $is_regex = $params->{is_regex
};
413 if ( ! $record ) { return; }
415 my @field_numbers = ();
416 my $current_field_number = 1;
417 FIELDS
: for my $field ( $record->field( $fieldName ) ) {
419 if ( $field->is_control_field ) {
420 push @subfield_values, $field->data;
424 ?
$field->subfield($subfieldName)
425 : map { $_->[1] } $field->subfields;
428 SUBFIELDS
: for my $subfield_value ( @subfield_values ) {
431 $is_regex and $subfield_value =~ m/$value/
433 $subfield_value eq $value
436 push @field_numbers, $current_field_number;
440 $current_field_number++;
443 return \
@field_numbers;
448 move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex [, $n ] ] );
450 Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
451 the value will be transformed by the given regex before being moved into the new field.
452 Example: $regex = 's/Old Text/Replacement Text/'
454 If $n is passed, only the Nth field will be moved. $n = 1
455 will move the first repeatable field, $n = 3 will move the third.
461 my $record = $params->{record
};
462 my $fromFieldName = $params->{from_field
};
463 my $fromSubfieldName = $params->{from_subfield
};
464 my $toFieldName = $params->{to_field
};
465 my $toSubfieldName = $params->{to_subfield
};
466 my $regex = $params->{regex
};
467 my $field_numbers = $params->{field_numbers
} // [];
469 if ( not $fromSubfieldName
470 or $fromSubfieldName eq ''
471 or not $toSubfieldName
472 or $toSubfieldName eq '' ) {
475 from_field
=> $fromFieldName,
476 to_field
=> $toFieldName,
478 field_numbers
=> $field_numbers,
485 from_field
=> $fromFieldName,
486 from_subfield
=> $fromSubfieldName,
487 to_field
=> $toFieldName,
488 to_subfield
=> $toSubfieldName,
490 field_numbers
=> $field_numbers,
499 _delete_field( $record, $fieldName[, $subfieldName [, $n ] ] );
501 Deletes the given field.
503 If $n is passed, only the Nth field will be deleted. $n = 1
504 will delete the first repeatable field, $n = 3 will delete the third.
510 my $record = $params->{record
};
511 my $fieldName = $params->{field
};
512 my $subfieldName = $params->{subfield
};
513 my $field_numbers = $params->{field_numbers
} // [];
515 if ( not $subfieldName or $subfieldName eq '' ) {
516 _delete_field
({ record
=> $record, field
=> $fieldName, field_numbers
=> $field_numbers });
518 _delete_subfield
({ record
=> $record, field
=> $fieldName, subfield
=> $subfieldName, field_numbers
=> $field_numbers });
524 my $record = $params->{record
};
525 my $fieldName = $params->{field
};
526 my $field_numbers = $params->{field_numbers
} // [];
528 my @fields = $record->field( $fieldName );
530 if ( @
$field_numbers ) {
531 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
533 foreach my $field ( @fields ) {
534 $record->delete_field( $field );
538 sub _delete_subfield
{
540 my $record = $params->{record
};
541 my $fieldName = $params->{field
};
542 my $subfieldName = $params->{subfield
};
543 my $field_numbers = $params->{field_numbers
} // [];
545 my @fields = $record->field( $fieldName );
547 if ( @
$field_numbers ) {
548 @fields = map { $_ <= @fields ?
$fields[ $_ - 1 ] : () } @
$field_numbers;
551 foreach my $field ( @fields ) {
552 $field->delete_subfield( code
=> $subfieldName );
557 sub _copy_move_field
{
559 my $record = $params->{record
};
560 my $fromFieldName = $params->{from_field
};
561 my $toFieldName = $params->{to_field
};
562 my $regex = $params->{regex
};
563 my $field_numbers = $params->{field_numbers
} // [];
564 my $action = $params->{action
} || 'copy';
566 my @from_fields = $record->field( $fromFieldName );
567 if ( @
$field_numbers ) {
568 @from_fields = map { $_ <= @from_fields ?
$from_fields[ $_ - 1 ] : () } @
$field_numbers;
572 for my $from_field ( @from_fields ) {
573 my $new_field = $from_field->clone;
574 $new_field->{_tag
} = $toFieldName; # Should be replaced by set_tag, introduced by MARC::Field 2.0.4
575 if ( $regex and $regex->{search
} ) {
576 for my $subfield ( $new_field->subfields ) {
577 my $value = $subfield->[1];
578 ( $value ) = _modify_values
({ values => [ $value ], regex
=> $regex });
579 $new_field->update( $subfield->[0], $value );
582 if ( $action eq 'move' ) {
583 $record->delete_field( $from_field )
585 elsif ( $action eq 'replace' ) {
586 my @to_fields = $record->field( $toFieldName );
588 $record->delete_field( $to_fields[0] );
591 push @new_fields, $new_field;
593 $record->append_fields( @new_fields );
596 sub _copy_move_subfield
{
598 my $record = $params->{record
};
599 my $fromFieldName = $params->{from_field
};
600 my $fromSubfieldName = $params->{from_subfield
};
601 my $toFieldName = $params->{to_field
};
602 my $toSubfieldName = $params->{to_subfield
};
603 my $regex = $params->{regex
};
604 my $field_numbers = $params->{field_numbers
} // [];
605 my $action = $params->{action
} || 'copy';
607 my @values = read_field
({ record
=> $record, field
=> $fromFieldName, subfield
=> $fromSubfieldName });
608 if ( @
$field_numbers ) {
609 @values = map { $_ <= @values ?
$values[ $_ - 1 ] : () } @
$field_numbers;
611 _modify_values
({ values => \
@values, regex
=> $regex });
612 my $dont_erase = $action eq 'copy' ?
1 : 0;
613 _update_subfield
({ record
=> $record, field
=> $toFieldName, subfield
=> $toSubfieldName, values => \
@values, dont_erase
=> $dont_erase });
615 # And delete if it's a move
616 if ( $action eq 'move' ) {
619 field
=> $fromFieldName,
620 subfield
=> $fromSubfieldName,
621 field_numbers
=> $field_numbers,
628 my $values = $params->{values};
629 my $regex = $params->{regex
};
631 if ( $regex and $regex->{search
} ) {
632 $regex->{modifiers
} //= q
||;
633 my @available_modifiers = qw( i g );
635 for my $modifier ( split //, $regex->{modifiers
} ) {
636 $modifiers .= $modifier
637 if grep {/$modifier/} @available_modifiers;
639 foreach my $value ( @
$values ) {
640 if ( $modifiers =~ m/^(ig|gi)$/ ) {
641 $value =~ s/$regex->{search}/$regex->{replace}/ig;
643 elsif ( $modifiers eq 'i' ) {
644 $value =~ s/$regex->{search}/$regex->{replace}/i;
646 elsif ( $modifiers eq 'g' ) {
647 $value =~ s/$regex->{search}/$regex->{replace}/g;
650 $value =~ s/$regex->{search}/$regex->{replace}/;