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
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,
118 my $record = $params->{record};
119 my $fieldName = $params->{field};
120 my $subfieldName = $params->{subfield};
121 my @values = @{ $params->{values} };
122 my $field_numbers = $params->{field_numbers} // [];
124 if ( ! ( $record && $fieldName ) ) { return; }
126 if ( not $subfieldName or $subfieldName eq '' ) {
127 # FIXME I'm not sure the actual implementation is correct.
128 die "This action is not implemented yet";
129 #_update_field({ record => $record, field => $fieldName, values => \@values });
131 _update_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, values => \@values, field_numbers => $field_numbers });
137 my $record = $params->{record};
138 my $fieldName = $params->{field};
139 my @values = @{ $params->{values} };
142 if ( my @fields = $record->field( $fieldName ) ) {
143 @values = ($values[0]) x scalar( @fields )
145 foreach my $field ( @fields ) {
146 $field->update( $values[$i++] );
149 ## Field does not exists, create it
150 if ( $fieldName < 10 ) {
151 foreach my $value ( @values ) {
152 my $field = MARC::Field->new( $fieldName, $value );
153 $record->append_fields( $field );
156 warn "Invalid operation, trying to add a new field without subfield";
161 sub _update_subfield {
163 my $record = $params->{record};
164 my $fieldName = $params->{field};
165 my $subfieldName = $params->{subfield};
166 my @values = @{ $params->{values} };
167 my $dont_erase = $params->{dont_erase};
168 my $field_numbers = $params->{field_numbers} // [];
171 my @fields = $record->field( $fieldName );
173 if ( @$field_numbers ) {
174 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
178 unless ( $dont_erase ) {
179 @values = ($values[0]) x scalar( @fields )
181 foreach my $field ( @fields ) {
182 $field->update( "$subfieldName" => $values[$i++] );
185 if ( $i <= scalar ( @values ) - 1 ) {
186 foreach my $field ( @fields ) {
187 foreach my $j ( $i .. scalar( @values ) - 1) {
188 $field->add_subfields( "$subfieldName" => $values[$j] );
193 ## Field does not exist, create it.
194 foreach my $value ( @values ) {
195 my $field = MARC::Field->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
196 $record->append_fields( $field );
203 my @values = read_field( $record, $fieldName[, $subfieldName, [, $n ] ] );
205 Returns an array of field values for the given field and subfield
207 If $n is given, it will return only the $nth value of the array.
208 E.g. If $n = 1, it return the 1st value, if $n = 3, it will return the 3rd value.
214 my $record = $params->{record};
215 my $fieldName = $params->{field};
216 my $subfieldName = $params->{subfield};
217 my $field_numbers = $params->{field_numbers} // [];
219 if ( not $subfieldName or $subfieldName eq '' ) {
220 _read_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
222 _read_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
228 my $record = $params->{record};
229 my $fieldName = $params->{field};
230 my $field_numbers = $params->{field_numbers} // [];
232 my @fields = $record->field( $fieldName );
234 return unless @fields;
236 return map { $_->data() } @fields
240 if ( @$field_numbers ) {
241 for my $field_number ( @$field_numbers ) {
242 if ( $field_number <= scalar( @fields ) ) {
243 for my $sf ( $fields[$field_number - 1]->subfields ) {
244 push @values, $sf->[1];
249 foreach my $field ( @fields ) {
250 for my $sf ( $field->subfields ) {
251 push @values, $sf->[1];
261 my $record = $params->{record};
262 my $fieldName = $params->{field};
263 my $subfieldName = $params->{subfield};
264 my $field_numbers = $params->{field_numbers} // [];
266 my @fields = $record->field( $fieldName );
268 return unless @fields;
271 foreach my $field ( @fields ) {
272 my @sf = $field->subfield( $subfieldName );
273 push( @values, @sf );
276 if ( @values and @$field_numbers ) {
277 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
285 @field_numbers = field_exists( $record, $fieldName[, $subfieldName ]);
287 Returns the field numbers or an empty array.
293 my $record = $params->{record};
294 my $fieldName = $params->{field};
295 my $subfieldName = $params->{subfield};
297 if ( ! $record ) { return; }
299 my @field_numbers = ();
300 my $current_field_number = 1;
301 for my $field ( $record->field( $fieldName ) ) {
302 if ( $subfieldName ) {
303 push @field_numbers, $current_field_number
304 if $field->subfield( $subfieldName );
306 push @field_numbers, $current_field_number;
308 $current_field_number++;
311 return \@field_numbers;
316 $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
318 Returns true if the field equals the given value, false otherwise.
320 If a regular expression ( $regex ) is supplied, the value will be compared using
321 the given regex. Example: $regex = 'sought_text'
327 my $record = $params->{record};
328 my $value = $params->{value};
329 my $fieldName = $params->{field};
330 my $subfieldName = $params->{subfield};
331 my $is_regex = $params->{is_regex};
333 if ( ! $record ) { return; }
335 my @field_numbers = ();
336 my $current_field_number = 1;
337 FIELDS: for my $field ( $record->field( $fieldName ) ) {
338 my @subfield_values = $subfieldName
339 ? $field->subfield( $subfieldName )
340 : map { $_->[1] } $field->subfields;
342 SUBFIELDS: for my $subfield_value ( @subfield_values ) {
345 $is_regex and $subfield_value =~ m/$value/
347 $subfield_value eq $value
350 push @field_numbers, $current_field_number;
354 $current_field_number++;
357 return \@field_numbers;
362 move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex [, $n ] ] );
364 Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
365 the value will be transformed by the given regex before being moved into the new field.
366 Example: $regex = 's/Old Text/Replacement Text/'
368 If $n is passed, only the Nth field will be moved. $n = 1
369 will move the first repeatable field, $n = 3 will move the third.
375 my $record = $params->{record};
376 my $fromFieldName = $params->{from_field};
377 my $fromSubfieldName = $params->{from_subfield};
378 my $toFieldName = $params->{to_field};
379 my $toSubfieldName = $params->{to_subfield};
380 my $regex = $params->{regex};
381 my $field_numbers = $params->{field_numbers} // [];
383 if ( not $fromSubfieldName
384 or $fromSubfieldName eq ''
385 or not $toSubfieldName
386 or $toSubfieldName eq '' ) {
389 from_field => $fromFieldName,
390 to_field => $toFieldName,
392 field_numbers => $field_numbers,
399 from_field => $fromFieldName,
400 from_subfield => $fromSubfieldName,
401 to_field => $toFieldName,
402 to_subfield => $toSubfieldName,
404 field_numbers => $field_numbers,
413 _delete_field( $record, $fieldName[, $subfieldName [, $n ] ] );
415 Deletes the given field.
417 If $n is passed, only the Nth field will be deleted. $n = 1
418 will delete the first repeatable field, $n = 3 will delete the third.
424 my $record = $params->{record};
425 my $fieldName = $params->{field};
426 my $subfieldName = $params->{subfield};
427 my $field_numbers = $params->{field_numbers} // [];
429 if ( not $subfieldName or $subfieldName eq '' ) {
430 _delete_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
432 _delete_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
438 my $record = $params->{record};
439 my $fieldName = $params->{field};
440 my $field_numbers = $params->{field_numbers} // [];
442 my @fields = $record->field( $fieldName );
444 if ( @$field_numbers ) {
445 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
447 foreach my $field ( @fields ) {
448 $record->delete_field( $field );
452 sub _delete_subfield {
454 my $record = $params->{record};
455 my $fieldName = $params->{field};
456 my $subfieldName = $params->{subfield};
457 my $field_numbers = $params->{field_numbers} // [];
459 my @fields = $record->field( $fieldName );
461 if ( @$field_numbers ) {
462 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
465 foreach my $field ( @fields ) {
466 $field->delete_subfield( code => $subfieldName );
471 sub _copy_move_field {
473 my $record = $params->{record};
474 my $fromFieldName = $params->{from_field};
475 my $toFieldName = $params->{to_field};
476 my $regex = $params->{regex};
477 my $field_numbers = $params->{field_numbers} // [];
478 my $action = $params->{action} || 'copy';
480 my @fields = $record->field( $fromFieldName );
481 if ( @$field_numbers ) {
482 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
485 for my $field ( @fields ) {
486 my $new_field = $field->clone;
487 $new_field->{_tag} = $toFieldName; # Should be replaced by set_tag, introduced by MARC::Field 2.0.4
488 if ( $regex and $regex->{search} ) {
489 for my $subfield ( $new_field->subfields ) {
490 my $value = $subfield->[1];
491 ( $value ) = _modify_values({ values => [ $value ], regex => $regex });
492 $new_field->update( $subfield->[0], $value );
495 $record->append_fields( $new_field );
496 $record->delete_field( $field )
497 if $action eq 'move';
501 sub _copy_move_subfield {
503 my $record = $params->{record};
504 my $fromFieldName = $params->{from_field};
505 my $fromSubfieldName = $params->{from_subfield};
506 my $toFieldName = $params->{to_field};
507 my $toSubfieldName = $params->{to_subfield};
508 my $regex = $params->{regex};
509 my $field_numbers = $params->{field_numbers} // [];
510 my $action = $params->{action} || 'copy';
512 my @values = read_field({ record => $record, field => $fromFieldName, subfield => $fromSubfieldName });
513 if ( @$field_numbers ) {
514 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
516 _modify_values({ values => \@values, regex => $regex });
517 my $dont_erase = $action eq 'copy' ? 1 : 0;
518 _update_subfield({ record => $record, field => $toFieldName, subfield => $toSubfieldName, values => \@values, dont_erase => $dont_erase });
520 # And delete if it's a move
521 if ( $action eq 'move' ) {
524 field => $fromFieldName,
525 subfield => $fromSubfieldName,
526 field_numbers => $field_numbers,
533 my $values = $params->{values};
534 my $regex = $params->{regex};
536 if ( $regex and $regex->{search} ) {
537 $regex->{modifiers} //= q||;
538 my @available_modifiers = qw( i g );
540 for my $modifier ( split //, $regex->{modifiers} ) {
541 $modifiers .= $modifier
542 if grep {/$modifier/} @available_modifiers;
544 foreach my $value ( @$values ) {
545 if ( $modifiers =~ m/^(ig|gi)$/ ) {
546 $value =~ s/$regex->{search}/$regex->{replace}/ig;
548 elsif ( $modifiers eq 'i' ) {
549 $value =~ s/$regex->{search}/$regex->{replace}/i;
551 elsif ( $modifiers eq 'g' ) {
552 $value =~ s/$regex->{search}/$regex->{replace}/g;
555 $value =~ s/$regex->{search}/$regex->{replace}/;