Bug 13451: Holidays.t assumes default data
[koha.git] / Koha / SimpleMARC.pm
blob3d99deca0619023b3e4b519f65a5d6963d0699ca
1 package Koha::SimpleMARC;
3 # Copyright 2009 Kyle M. Hall <kyle.m.hall@gmail.com>
5 use Modern::Perl;
7 #use MARC::Record;
9 require Exporter;
11 our @ISA = qw(Exporter);
12 our %EXPORT_TAGS = ( 'all' => [ qw(
14 ) ] );
16 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
18 our @EXPORT = qw(
19 read_field
20 update_field
21 copy_field
22 move_field
23 delete_field
24 field_exists
25 field_equals
28 our $VERSION = '0.01';
30 our $debug = 0;
32 =head1 NAME
34 SimpleMARC - Perl module for making simple MARC record alterations.
36 =head1 SYNOPSIS
38 use SimpleMARC;
40 =head1 DESCRIPTION
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.
48 =head1 AUTHOR
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.
60 =head1 FUNCTIONS
62 =head2 copy_field
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.
73 =cut
75 sub copy_field {
76 my ( $params ) = @_;
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 ''
90 ) {
91 _copy_field({
92 record => $record,
93 from_field => $fromFieldName,
94 to_field => $toFieldName,
95 regex => $regex,
96 field_numbers => $field_numbers,
97 });
98 } else {
99 _copy_subfield({
100 record => $record,
101 from_field => $fromFieldName,
102 from_subfield => $fromSubfieldName,
103 to_field => $toFieldName,
104 to_subfield => $toSubfieldName,
105 regex => $regex,
106 field_numbers => $field_numbers,
112 sub _copy_field {
113 my ( $params ) = @_;
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} // [];
120 _copy_move_field({
121 record => $record,
122 from_field => $fromFieldName,
123 to_field => $toFieldName,
124 regex => $regex,
125 field_numbers => $field_numbers,
129 sub _copy_subfield {
130 my ( $params ) = @_;
131 my $record = $params->{record};
132 my $fromFieldName = $params->{from_field};
133 my $fromSubfieldName = $params->{from_subfield};
134 my $toFieldName = $params->{to_field};
135 my $toSubfieldName = $params->{to_subfield};
136 my $regex = $params->{regex};
137 my $field_numbers = $params->{field_numbers} // [];
139 my @values = read_field({ record => $record, field => $fromFieldName, subfield => $fromSubfieldName });
140 if ( @$field_numbers ) {
141 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
143 _modify_values({ values => \@values, regex => $regex });
145 update_field({ record => $record, field => $toFieldName, subfield => $toSubfieldName, values => \@values });
148 sub update_field {
149 my ( $params ) = @_;
150 my $record = $params->{record};
151 my $fieldName = $params->{field};
152 my $subfieldName = $params->{subfield};
153 my @values = @{ $params->{values} };
154 my $field_numbers = $params->{field_numbers} // [];
156 if ( ! ( $record && $fieldName ) ) { return; }
158 if ( not $subfieldName or $subfieldName eq '' ) {
159 # FIXME I'm not sure the actual implementation is correct.
160 die "This action is not implemented yet";
161 #_update_field({ record => $record, field => $fieldName, values => \@values });
162 } else {
163 _update_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, values => \@values, field_numbers => $field_numbers });
167 sub _update_field {
168 my ( $params ) = @_;
169 my $record = $params->{record};
170 my $fieldName = $params->{field};
171 my @values = @{ $params->{values} };
173 my $i = 0;
174 if ( my @fields = $record->field( $fieldName ) ) {
175 @values = ($values[0]) x scalar( @fields )
176 if @values == 1;
177 foreach my $field ( @fields ) {
178 $field->update( $values[$i++] );
180 } else {
181 ## Field does not exists, create it
182 if ( $fieldName < 10 ) {
183 foreach my $value ( @values ) {
184 my $field = MARC::Field->new( $fieldName, $value );
185 $record->append_fields( $field );
187 } else {
188 warn "Invalid operation, trying to add a new field without subfield";
193 sub _update_subfield {
194 my ( $params ) = @_;
195 my $record = $params->{record};
196 my $fieldName = $params->{field};
197 my $subfieldName = $params->{subfield};
198 my @values = @{ $params->{values} };
199 my $dont_erase = $params->{dont_erase};
200 my $field_numbers = $params->{field_numbers} // [];
201 my $i = 0;
203 my @fields = $record->field( $fieldName );
205 if ( @$field_numbers ) {
206 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
209 if ( @fields ) {
210 unless ( $dont_erase ) {
211 @values = ($values[0]) x scalar( @fields )
212 if @values == 1;
213 foreach my $field ( @fields ) {
214 $field->update( "$subfieldName" => $values[$i++] );
217 if ( $i <= scalar ( @values ) - 1 ) {
218 foreach my $field ( @fields ) {
219 foreach my $j ( $i .. scalar( @values ) - 1) {
220 $field->add_subfields( "$subfieldName" => $values[$j] );
224 } else {
225 ## Field does not exist, create it.
226 foreach my $value ( @values ) {
227 my $field = MARC::Field->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
228 $record->append_fields( $field );
233 =head2 read_field
235 my @values = read_field( $record, $fieldName[, $subfieldName, [, $n ] ] );
237 Returns an array of field values for the given field and subfield
239 If $n is given, it will return only the $nth value of the array.
240 E.g. If $n = 1, it return the 1st value, if $n = 3, it will return the 3rd value.
242 =cut
244 sub read_field {
245 my ( $params ) = @_;
246 my $record = $params->{record};
247 my $fieldName = $params->{field};
248 my $subfieldName = $params->{subfield};
249 my $field_numbers = $params->{field_numbers} // [];
251 if ( not $subfieldName or $subfieldName eq '' ) {
252 _read_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
253 } else {
254 _read_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
258 sub _read_field {
259 my ( $params ) = @_;
260 my $record = $params->{record};
261 my $fieldName = $params->{field};
262 my $field_numbers = $params->{field_numbers} // [];
264 my @fields = $record->field( $fieldName );
266 return unless @fields;
268 return map { $_->data() } @fields
269 if $fieldName < 10;
271 my @values;
272 if ( @$field_numbers ) {
273 for my $field_number ( @$field_numbers ) {
274 if ( $field_number <= scalar( @fields ) ) {
275 for my $sf ( $fields[$field_number - 1]->subfields ) {
276 push @values, $sf->[1];
280 } else {
281 foreach my $field ( @fields ) {
282 for my $sf ( $field->subfields ) {
283 push @values, $sf->[1];
288 return @values;
291 sub _read_subfield {
292 my ( $params ) = @_;
293 my $record = $params->{record};
294 my $fieldName = $params->{field};
295 my $subfieldName = $params->{subfield};
296 my $field_numbers = $params->{field_numbers} // [];
298 my @fields = $record->field( $fieldName );
300 return unless @fields;
302 my @values;
303 foreach my $field ( @fields ) {
304 my @sf = $field->subfield( $subfieldName );
305 push( @values, @sf );
308 if ( @values and @$field_numbers ) {
309 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
312 return @values;
315 =head2 field_exists
317 @field_numbers = field_exists( $record, $fieldName[, $subfieldName ]);
319 Returns the field numbers or an empty array.
321 =cut
323 sub field_exists {
324 my ( $params ) = @_;
325 my $record = $params->{record};
326 my $fieldName = $params->{field};
327 my $subfieldName = $params->{subfield};
329 if ( ! $record ) { return; }
331 my @field_numbers = ();
332 my $current_field_number = 1;
333 for my $field ( $record->field( $fieldName ) ) {
334 if ( $subfieldName ) {
335 push @field_numbers, $current_field_number
336 if $field->subfield( $subfieldName );
337 } else {
338 push @field_numbers, $current_field_number;
340 $current_field_number++;
343 return \@field_numbers;
346 =head2 field_equals
348 $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
350 Returns true if the field equals the given value, false otherwise.
352 If a regular expression ( $regex ) is supplied, the value will be compared using
353 the given regex. Example: $regex = 'sought_text'
355 =cut
357 sub field_equals {
358 my ( $params ) = @_;
359 my $record = $params->{record};
360 my $value = $params->{value};
361 my $fieldName = $params->{field};
362 my $subfieldName = $params->{subfield};
363 my $is_regex = $params->{is_regex};
365 if ( ! $record ) { return; }
367 my @field_numbers = ();
368 my $current_field_number = 1;
369 FIELDS: for my $field ( $record->field( $fieldName ) ) {
370 my @subfield_values = $subfieldName
371 ? $field->subfield( $subfieldName )
372 : map { $_->[1] } $field->subfields;
374 SUBFIELDS: for my $subfield_value ( @subfield_values ) {
375 if (
377 $is_regex and $subfield_value =~ m/$value/
378 ) or (
379 $subfield_value eq $value
382 push @field_numbers, $current_field_number;
383 last SUBFIELDS;
386 $current_field_number++;
389 return \@field_numbers;
392 =head2 move_field
394 move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex [, $n ] ] );
396 Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
397 the value will be transformed by the given regex before being moved into the new field.
398 Example: $regex = 's/Old Text/Replacement Text/'
400 If $n is passed, only the Nth field will be moved. $n = 1
401 will move the first repeatable field, $n = 3 will move the third.
403 =cut
405 sub move_field {
406 my ( $params ) = @_;
407 my $record = $params->{record};
408 my $fromFieldName = $params->{from_field};
409 my $fromSubfieldName = $params->{from_subfield};
410 my $toFieldName = $params->{to_field};
411 my $toSubfieldName = $params->{to_subfield};
412 my $regex = $params->{regex};
413 my $field_numbers = $params->{field_numbers} // [];
415 if ( not $fromSubfieldName or $fromSubfieldName eq ''
416 or not $toSubfieldName or $toSubfieldName eq ''
418 _move_field({
419 record => $record,
420 from_field => $fromFieldName,
421 to_field => $toFieldName,
422 regex => $regex,
423 field_numbers => $field_numbers,
425 } else {
426 _move_subfield({
427 record => $record,
428 from_field => $fromFieldName,
429 from_subfield => $fromSubfieldName,
430 to_field => $toFieldName,
431 to_subfield => $toSubfieldName,
432 regex => $regex,
433 field_numbers => $field_numbers,
438 sub _move_field {
439 my ( $params ) = @_;
440 my $record = $params->{record};
441 my $fromFieldName = $params->{from_field};
442 my $toFieldName = $params->{to_field};
443 my $regex = $params->{regex};
444 my $field_numbers = $params->{field_numbers} // [];
446 _copy_move_field({
447 record => $record,
448 from_field => $fromFieldName,
449 to_field => $toFieldName,
450 regex => $regex,
451 field_numbers => $field_numbers,
452 action => 'move',
456 sub _move_subfield {
457 my ( $params ) = @_;
458 my $record = $params->{record};
459 my $fromFieldName = $params->{from_field};
460 my $fromSubfieldName = $params->{from_subfield};
461 my $toFieldName = $params->{to_field};
462 my $toSubfieldName = $params->{to_subfield};
463 my $regex = $params->{regex};
464 my $field_numbers = $params->{field_numbers} // [];
466 # Copy
467 my @values = read_field({ record => $record, field => $fromFieldName, subfield => $fromSubfieldName });
468 if ( @$field_numbers ) {
469 @values = map { $_ <= @values ? $values[ $_ - 1 ] : () } @$field_numbers;
471 _modify_values({ values => \@values, regex => $regex });
472 _update_subfield({ record => $record, field => $toFieldName, subfield => $toSubfieldName, dont_erase => 1, values => \@values });
474 # And delete
475 _delete_subfield({
476 record => $record,
477 field => $fromFieldName,
478 subfield => $fromSubfieldName,
479 field_numbers => $field_numbers,
483 =head2 _delete_field
485 _delete_field( $record, $fieldName[, $subfieldName [, $n ] ] );
487 Deletes the given field.
489 If $n is passed, only the Nth field will be deleted. $n = 1
490 will delete the first repeatable field, $n = 3 will delete the third.
492 =cut
494 sub delete_field {
495 my ( $params ) = @_;
496 my $record = $params->{record};
497 my $fieldName = $params->{field};
498 my $subfieldName = $params->{subfield};
499 my $field_numbers = $params->{field_numbers} // [];
501 if ( not $subfieldName or $subfieldName eq '' ) {
502 _delete_field({ record => $record, field => $fieldName, field_numbers => $field_numbers });
503 } else {
504 _delete_subfield({ record => $record, field => $fieldName, subfield => $subfieldName, field_numbers => $field_numbers });
508 sub _delete_field {
509 my ( $params ) = @_;
510 my $record = $params->{record};
511 my $fieldName = $params->{field};
512 my $field_numbers = $params->{field_numbers} // [];
514 my @fields = $record->field( $fieldName );
516 if ( @$field_numbers ) {
517 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
519 foreach my $field ( @fields ) {
520 $record->delete_field( $field );
524 sub _delete_subfield {
525 my ( $params ) = @_;
526 my $record = $params->{record};
527 my $fieldName = $params->{field};
528 my $subfieldName = $params->{subfield};
529 my $field_numbers = $params->{field_numbers} // [];
531 my @fields = $record->field( $fieldName );
533 if ( @$field_numbers ) {
534 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
537 foreach my $field ( @fields ) {
538 $field->delete_subfield( code => $subfieldName );
543 sub _copy_move_field {
544 my ( $params ) = @_;
545 my $record = $params->{record};
546 my $fromFieldName = $params->{from_field};
547 my $toFieldName = $params->{to_field};
548 my $regex = $params->{regex};
549 my $field_numbers = $params->{field_numbers} // [];
550 my $action = $params->{action} || 'copy';
552 my @fields = $record->field( $fromFieldName );
553 if ( @$field_numbers ) {
554 @fields = map { $_ <= @fields ? $fields[ $_ - 1 ] : () } @$field_numbers;
557 for my $field ( @fields ) {
558 my $new_field = $field->clone;
559 $new_field->{_tag} = $toFieldName; # Should be replaced by set_tag, introduced by MARC::Field 2.0.4
560 if ( $regex and $regex->{search} ) {
561 for my $subfield ( $new_field->subfields ) {
562 my $value = $subfield->[1];
563 ( $value ) = _modify_values({ values => [ $value ], regex => $regex });
564 $new_field->update( $subfield->[0], $value );
567 $record->append_fields( $new_field );
568 $record->delete_field( $field )
569 if $action eq 'move';
573 sub _modify_values {
574 my ( $params ) = @_;
575 my $values = $params->{values};
576 my $regex = $params->{regex};
578 if ( $regex and $regex->{search} ) {
579 $regex->{modifiers} //= q||;
580 my @available_modifiers = qw( i g );
581 my $modifiers = q||;
582 for my $modifier ( split //, $regex->{modifiers} ) {
583 $modifiers .= $modifier
584 if grep {/$modifier/} @available_modifiers;
586 foreach my $value ( @$values ) {
587 if ( $modifiers =~ m/^(ig|gi)$/ ) {
588 $value =~ s/$regex->{search}/$regex->{replace}/ig;
590 elsif ( $modifiers eq 'i' ) {
591 $value =~ s/$regex->{search}/$regex->{replace}/i;
593 elsif ( $modifiers eq 'g' ) {
594 $value =~ s/$regex->{search}/$regex->{replace}/g;
596 else {
597 $value =~ s/$regex->{search}/$regex->{replace}/;
601 return @$values;
604 __END__