From: djfiander Date: Sat, 14 Nov 2009 19:59:50 +0000 (+0000) Subject: Whitespace patch to bring MFHD code into line with new perltidy standard X-Git-Tag: kcls-grey-screen-prod1~3003 X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=5bea1db203070bd85692671aec6e07c7b8ab2ffa;p=evergreen%2Fequinox.git Whitespace patch to bring MFHD code into line with new perltidy standard git-svn-id: svn://svn.open-ils.org/ILS/trunk@14917 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm index e71c54eccb..f329d7baf7 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm @@ -12,58 +12,60 @@ use OpenILS::Utils::MFHD::Holding; sub new { my $proto = shift; my $class = ref($proto) || $proto; - my $self = shift; + my $self = shift; $self->{_mfhd_CAPTIONS} = {}; $self->{_mfhd_COMPRESSIBLE} = (substr($self->leader, 17, 1) =~ /[45]/); foreach my $field ('853', '854', '855') { - my $captions = {}; - foreach my $caption ($self->field($field)) { - my $cap_id; - - $cap_id = $caption->subfield('8') || '0'; - - if (exists $captions->{$cap_id}) { - carp "Multiple MFHD captions with label '$cap_id'"; - } - - $captions->{$cap_id} = new MFHD::Caption($caption); - if ($self->{_mfhd_COMPRESSIBLE}) { - $self->{_mfhd_COMPRESSIBLE} &&= $captions->{$cap_id}->compressible; - } - } - $self->{_mfhd_CAPTIONS}->{$field} = $captions; + my $captions = {}; + foreach my $caption ($self->field($field)) { + my $cap_id; + + $cap_id = $caption->subfield('8') || '0'; + + if (exists $captions->{$cap_id}) { + carp "Multiple MFHD captions with label '$cap_id'"; + } + + $captions->{$cap_id} = new MFHD::Caption($caption); + if ($self->{_mfhd_COMPRESSIBLE}) { + $self->{_mfhd_COMPRESSIBLE} &&= + $captions->{$cap_id}->compressible; + } + } + $self->{_mfhd_CAPTIONS}->{$field} = $captions; } foreach my $field ('863', '864', '865') { - my $holdings = {}; - my $cap_field; - - ($cap_field = $field) =~ s/6/5/; - - foreach my $hfield ($self->field($field)) { - my ($linkage, $link_id, $seqno); - my $holding; - - $linkage = $hfield->subfield('8'); - ($link_id, $seqno) = split(/\./, $linkage); - - if (!exists $holdings->{$link_id}) { - $holdings->{$link_id} = {}; - } - $holding = new MFHD::Holding($seqno, $hfield, - $self->{_mfhd_CAPTIONS}->{$cap_field}->{$link_id}); - $holdings->{$link_id}->{$seqno} = $holding; - - if ($self->{_mfhd_COMPRESSIBLE}) { - $self->{_mfhd_COMPRESSIBLE} &&= $holding->validate; - } - } - $self->{_mfhd_HOLDINGS}->{$field} = $holdings; + my $holdings = {}; + my $cap_field; + + ($cap_field = $field) =~ s/6/5/; + + foreach my $hfield ($self->field($field)) { + my ($linkage, $link_id, $seqno); + my $holding; + + $linkage = $hfield->subfield('8'); + ($link_id, $seqno) = split(/\./, $linkage); + + if (!exists $holdings->{$link_id}) { + $holdings->{$link_id} = {}; + } + $holding = + new MFHD::Holding($seqno, $hfield, + $self->{_mfhd_CAPTIONS}->{$cap_field}->{$link_id}); + $holdings->{$link_id}->{$seqno} = $holding; + + if ($self->{_mfhd_COMPRESSIBLE}) { + $self->{_mfhd_COMPRESSIBLE} &&= $holding->validate; + } + } + $self->{_mfhd_HOLDINGS}->{$field} = $holdings; } - bless ($self, $class); + bless($self, $class); return $self; } @@ -74,18 +76,20 @@ sub compressible { } sub captions { - my $self = shift; + my $self = shift; my $field = shift; - return sort keys %{$self->{_mfhd_CAPTIONS}->{$field}} + return sort keys %{$self->{_mfhd_CAPTIONS}->{$field}}; } sub holdings { - my $self = shift; + my $self = shift; my $field = shift; my $capid = shift; - return sort {$a->seqno <=> $b->seqno} values %{$self->{_mfhd_HOLDINGS}->{$field}->{$capid}}; + return + sort { $a->seqno <=> $b->seqno } + values %{$self->{_mfhd_HOLDINGS}->{$field}->{$capid}}; } 1; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm index 1b395c693b..c7cbb26353 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm @@ -9,64 +9,65 @@ use OpenILS::Utils::MFHD::Date; use base 'MARC::Field'; -sub new -{ - my $proto = shift; - my $class = ref($proto) || $proto; - my $self = shift; +sub new { + my $proto = shift; + my $class = ref($proto) || $proto; + my $self = shift; my $last_enum = undef; - $self->{_mfhdc_ENUMS} = {}; - $self->{_mfhdc_CHRONS} = {}; - $self->{_mfhdc_PATTERN} = {}; - $self->{_mfhdc_COPY} = undef; - $self->{_mfhdc_UNIT} = undef; - $self->{_mfhdc_COMPRESSIBLE} = 1; # until proven otherwise + $self->{_mfhdc_ENUMS} = {}; + $self->{_mfhdc_CHRONS} = {}; + $self->{_mfhdc_PATTERN} = {}; + $self->{_mfhdc_COPY} = undef; + $self->{_mfhdc_UNIT} = undef; + $self->{_mfhdc_COMPRESSIBLE} = 1; # until proven otherwise foreach my $subfield ($self->subfields) { - my ($key, $val) = @$subfield; - if ($key eq '8') { - $self->{LINK} = $val; - } elsif ($key =~ /[a-h]/) { - # Enumeration Captions - $self->{_mfhdc_ENUMS}->{$key} = {CAPTION => $val, - COUNT => undef, - RESTART => undef}; - if ($key =~ /[ag]/) { - $last_enum = undef; - } else { - $last_enum = $key; - } - } elsif ($key =~ /[i-m]/) { - # Chronology captions - $self->{_mfhdc_CHRONS}->{$key} = $val; - } elsif ($key eq 'u') { - # Bib units per next higher enumeration level - carp('$u specified for top-level enumeration') - unless defined($last_enum); - $self->{_mfhdc_ENUMS}->{$last_enum}->{COUNT} = $val; - } elsif ($key eq 'v') { - carp '$v specified for top-level enumeration' - unless defined($last_enum); - $self->{_mfhdc_ENUMS}->{$last_enum}->{RESTART} = ($val eq 'r'); - } elsif ($key =~ /[npwz]/) { - # Publication Pattern info ('o' == type of unit, 'q'..'t' undefined) - $self->{_mfhdc_PATTERN}->{$key} = $val; - } elsif ($key =~ /x/) { - # Calendar change can have multiple comma-separated values - $self->{_mfhdc_PATTERN}->{x} = [split /,/, $val]; - } elsif ($key eq 'y') { - $self->{_mfhdc_PATTERN}->{y} = {} - unless exists $self->{_mfhdc_PATTERN}->{y}; - update_pattern($self, $val); - } elsif ($key eq 'o') { - # Type of unit - $self->{_mfhdc_UNIT} = $val; - } elsif ($key eq 't') { - $self->{_mfhdc_COPY} = $val; - } else { - carp "Unknown caption subfield '$key'"; - } + my ($key, $val) = @$subfield; + if ($key eq '8') { + $self->{LINK} = $val; + } elsif ($key =~ /[a-h]/) { + # Enumeration Captions + $self->{_mfhdc_ENUMS}->{$key} = { + CAPTION => $val, + COUNT => undef, + RESTART => undef + }; + if ($key =~ /[ag]/) { + $last_enum = undef; + } else { + $last_enum = $key; + } + } elsif ($key =~ /[i-m]/) { + # Chronology captions + $self->{_mfhdc_CHRONS}->{$key} = $val; + } elsif ($key eq 'u') { + # Bib units per next higher enumeration level + carp('$u specified for top-level enumeration') + unless defined($last_enum); + $self->{_mfhdc_ENUMS}->{$last_enum}->{COUNT} = $val; + } elsif ($key eq 'v') { + carp '$v specified for top-level enumeration' + unless defined($last_enum); + $self->{_mfhdc_ENUMS}->{$last_enum}->{RESTART} = ($val eq 'r'); + } elsif ($key =~ /[npwz]/) { + # Publication Pattern info ('o' == type of unit, 'q'..'t' undefined) + $self->{_mfhdc_PATTERN}->{$key} = $val; + } elsif ($key =~ /x/) { + # Calendar change can have multiple comma-separated values + $self->{_mfhdc_PATTERN}->{x} = [split /,/, $val]; + } elsif ($key eq 'y') { + $self->{_mfhdc_PATTERN}->{y} = {} + unless exists $self->{_mfhdc_PATTERN}->{y}; + update_pattern($self, $val); + } elsif ($key eq 'o') { + # Type of unit + $self->{_mfhdc_UNIT} = $val; + } elsif ($key eq 't') { + $self->{_mfhdc_COPY} = $val; + } else { + carp "Unknown caption subfield '$key'"; + } } # subsequent levels of enumeration (primary and alternate) @@ -74,15 +75,16 @@ sub new # of "issues" per "volume", or whether numbering of issues # restarts, then we can't compress. foreach my $key ('b', 'c', 'd', 'e', 'f', 'h') { - if (exists $self->{_mfhdc_ENUMS}->{$key}) { - my $pattern = $self->{_mfhdc_ENUMS}->{$key}; - if (!$pattern->{RESTART} || !$pattern->{COUNT} - || ($pattern->{COUNT} eq 'var') - || ($pattern->{COUNT} eq 'und')) { - $self->{_mfhdc_COMPRESSIBLE} = 0; - last; - } - } + if (exists $self->{_mfhdc_ENUMS}->{$key}) { + my $pattern = $self->{_mfhdc_ENUMS}->{$key}; + if ( !$pattern->{RESTART} + || !$pattern->{COUNT} + || ($pattern->{COUNT} eq 'var') + || ($pattern->{COUNT} eq 'und')) { + $self->{_mfhdc_COMPRESSIBLE} = 0; + last; + } + } } my $pat = $self->{_mfhdc_PATTERN}; @@ -90,25 +92,27 @@ sub new # Sanity check publication frequency vs publication pattern: # if the frequency is a number, then the pattern better # have that number of values associated with it. - if (exists($pat->{w}) && ($pat->{w} =~ /^\d+$/) - && ($pat->{w} != scalar(@{$pat->{y}->{p}}))) { - carp("Caption::new: publication frequency '$pat->{w}' != publication pattern @{$pat->{y}->{p}}"); + if ( exists($pat->{w}) + && ($pat->{w} =~ /^\d+$/) + && ($pat->{w} != scalar(@{$pat->{y}->{p}}))) { + carp( +"Caption::new: publication frequency '$pat->{w}' != publication pattern @{$pat->{y}->{p}}" + ); } - # If there's a $x subfield and a $j, then it's compressible if (exists $pat->{x} && exists $self->{_mfhdc_CHRONS}->{'j'}) { - $self->{_mfhdc_COMPRESSIBLE} = 1; + $self->{_mfhdc_COMPRESSIBLE} = 1; } - bless ($self, $class); + bless($self, $class); return $self; } sub update_pattern { - my $self = shift; - my $val = shift; + my $self = shift; + my $val = shift; my $pathash = $self->{_mfhdc_PATTERN}->{y}; my ($pubcode, $pat) = unpack("a1a*", $val); @@ -117,7 +121,7 @@ sub update_pattern { } sub decode_pattern { - my $self = shift; + my $self = shift; my $pattern = $self->{_mfhdc_PATTERN}->{y}; # XXX WRITE ME (?) @@ -131,37 +135,37 @@ sub compressible { sub chrons { my $self = shift; - my $key = shift; + my $key = shift; if (exists $self->{_mfhdc_CHRONS}->{$key}) { - return $self->{_mfhdc_CHRONS}->{$key}; + return $self->{_mfhdc_CHRONS}->{$key}; } else { - return undef; + return undef; } } sub capfield { my $self = shift; - my $key = shift; + my $key = shift; if (exists $self->{_mfhdc_ENUMS}->{$key}) { - return $self->{_mfhdc_ENUMS}->{$key}; + return $self->{_mfhdc_ENUMS}->{$key}; } elsif (exists $self->{_mfhdc_CHRONS}->{$key}) { - return $self->{_mfhdc_CHRONS}->{$key}; + return $self->{_mfhdc_CHRONS}->{$key}; } else { - return undef; + return undef; } } sub capstr { my $self = shift; - my $key = shift; - my $val = $self->capfield($key); + my $key = shift; + my $val = $self->capfield($key); if (ref $val) { - return $val->{CAPTION}; + return $val->{CAPTION}; } else { - return $val; + return $val; } } @@ -188,46 +192,47 @@ sub enumeration_is_chronology { my $self = shift; # There is always a '$a' subfield in well-formed fields. - return 0 if exists $self->{_mfhdc_CHRONS}->{i} - || exists $self->{_mfhdc_PATTERN}->{x}; + return 0 + if exists $self->{_mfhdc_CHRONS}->{i} + || exists $self->{_mfhdc_PATTERN}->{x}; - foreach my $key ('a' .. 'f') { - my $enum; + foreach my $key ('a'..'f') { + my $enum; - last if !exists $self->{_mfhdc_ENUMS}->{$key}; + last if !exists $self->{_mfhdc_ENUMS}->{$key}; - $enum = $self->{_mfhdc_ENUMS}->{$key}; - return 0 if defined $enum->{COUNT} || defined $enum->{RESTART}; + $enum = $self->{_mfhdc_ENUMS}->{$key}; + return 0 if defined $enum->{COUNT} || defined $enum->{RESTART}; } return (exists $self->{_mfhdc_PATTERN}->{w}); } sub regularity_match { - my $self = shift; + my $self = shift; my $pubcode = shift; - my @date = @_; + my @date = @_; # we can't match something that doesn't exist. return 0 if !exists $self->{_mfhdc_PATTERN}->{y}->{$pubcode}; foreach my $regularity (@{$self->{_mfhdc_PATTERN}->{y}->{$pubcode}}) { - my $chroncode= substr($regularity, 0, 1); - my $matchfunc = MFHD::Date::dispatch($chroncode); - my @pats = split(/,/, substr($regularity, 1)); - - if (!defined $matchfunc) { - carp "Unrecognized chroncode '$chroncode'"; - return 0; - } - - # XXX WRITE ME - foreach my $pat (@pats) { - $pat =~ s|/.+||; # If it's a combined date, match the start - if ($matchfunc->($pat, @date)) { - return 1; - } - } + my $chroncode = substr($regularity, 0, 1); + my $matchfunc = MFHD::Date::dispatch($chroncode); + my @pats = split(/,/, substr($regularity, 1)); + + if (!defined $matchfunc) { + carp "Unrecognized chroncode '$chroncode'"; + return 0; + } + + # XXX WRITE ME + foreach my $pat (@pats) { + $pat =~ s|/.+||; # If it's a combined date, match the start + if ($matchfunc->($pat, @date)) { + return 1; + } + } } return 0; @@ -237,8 +242,8 @@ sub is_omitted { my $self = shift; my @date = @_; -# printf("# is_omitted: testing date %s: %d\n", join('/', @date), -# $self->regularity_match('o', @date)); + # printf("# is_omitted: testing date %s: %d\n", join('/', @date), + # $self->regularity_match('o', @date)); return $self->regularity_match('o', @date); } @@ -257,28 +262,27 @@ sub is_combined { } sub enum_is_combined { - my $self = shift; + my $self = shift; my $subfield = shift; - my $iss = shift; - my $level = ord($subfield) - ord('a') + 1; + my $iss = shift; + my $level = ord($subfield) - ord('a') + 1; return 0 if !exists $self->{_mfhdc_PATTERN}->{y}->{c}; foreach my $regularity (@{$self->{_mfhdc_PATTERN}->{y}->{c}}) { - next unless $regularity =~ m/^e$level/o; + next unless $regularity =~ m/^e$level/o; - my @pats = split(/,/, substr($regularity, 2)); + my @pats = split(/,/, substr($regularity, 2)); - foreach my $pat (@pats) { - $pat =~ s|/.+||; # if it's a combined issue, match the start - return 1 if ($iss eq $pat); - } + foreach my $pat (@pats) { + $pat =~ s|/.+||; # if it's a combined issue, match the start + return 1 if ($iss eq $pat); + } } return 0; } - # Test to see if $dt1 is on or after $dt2 # if length(@{$dt2} == 2, then just month/day are compared # if length(@{$dt2} == 1, then just the months are compared @@ -288,28 +292,28 @@ sub on_or_after { # printf("# on_or_after(%s, %s): ", join('/', @{$dt1}), join('/', @{$dt2})); - foreach my $i (0..(scalar(@{$dt2})-1)) { - if ($dt1->[$i] > $dt2->[$i]) { -# printf("after - pass\n"); - # $dt1 occurs AFTER $dt2 - return 1; - } elsif ($dt1->[$i] < $dt2->[$i]) { -# printf("before - fail\n"); - # $dt1 occurs BEFORE $dt2 - return 0; - } - # both are still equal, keep going + foreach my $i (0..(scalar(@{$dt2}) - 1)) { + if ($dt1->[$i] > $dt2->[$i]) { + # printf("after - pass\n"); + # $dt1 occurs AFTER $dt2 + return 1; + } elsif ($dt1->[$i] < $dt2->[$i]) { + # printf("before - fail\n"); + # $dt1 occurs BEFORE $dt2 + return 0; + } + # both are still equal, keep going } # We fell out of the loop with them being equal, so it's 'on' -# printf("on - pass\n"); + # printf("on - pass\n"); return 1; } sub calendar_increment { - my $self = shift; - my $cur = shift; - my $new = shift; + my $self = shift; + my $cur = shift; + my $new = shift; my $cal_change = $self->calendar_change; my $month; my $day; @@ -318,62 +322,64 @@ sub calendar_increment { # A calendar change is defined, need to check if it applies if (scalar(@{$new}) == 1) { - carp "Can't calculate date change for ", $self->as_string; - return 0; + carp "Can't calculate date change for ", $self->as_string; + return 0; } foreach my $change (@{$cal_change}) { - my $incr; - - if (length($change) == 2) { - $month = $change; - } elsif (length($change) == 4) { - ($month, $day) = unpack("a2a2", $change); - } - -# printf("# calendar_increment('%s', '%s'): change on '%s/%s'\n", -# join('/', @{$cur}), join('/', @{$new}), -# $month, defined($day) ? $day : 'UNDEF'); - - if ($cur->[0] == $new->[0]) { - # Same year, so a 'simple' month/day comparison will be fine - $incr = (!on_or_after([$cur->[1], $cur->[2]], [$month, $day]) - && on_or_after([$new->[1], $new->[2]], [$month, $day])); - } else { - # @cur is in the year before @new. There are - # two possible cases for the calendar change date that - # indicate that it's time to change the volume: - # (1) the change date is AFTER @cur in the year, or - # (2) the change date is BEFORE @new in the year. - # - # -------|------|------X------|------| - # @cur (1) Jan 1 (2) @new - - $incr = (on_or_after([$new->[1], $new->[2]], [$month, $day]) - || !on_or_after([$cur->[1], $cur->[2]], [$month, $day])); - } - return $incr if $incr; + my $incr; + + if (length($change) == 2) { + $month = $change; + } elsif (length($change) == 4) { + ($month, $day) = unpack("a2a2", $change); + } + + # printf("# calendar_increment('%s', '%s'): change on '%s/%s'\n", + # join('/', @{$cur}), join('/', @{$new}), + # $month, defined($day) ? $day : 'UNDEF'); + + if ($cur->[0] == $new->[0]) { + # Same year, so a 'simple' month/day comparison will be fine + $incr = + ( !on_or_after([$cur->[1], $cur->[2]], [$month, $day]) + && on_or_after([$new->[1], $new->[2]], [$month, $day])); + } else { + # @cur is in the year before @new. There are + # two possible cases for the calendar change date that + # indicate that it's time to change the volume: + # (1) the change date is AFTER @cur in the year, or + # (2) the change date is BEFORE @new in the year. + # + # -------|------|------X------|------| + # @cur (1) Jan 1 (2) @new + + $incr = + (on_or_after([$new->[1], $new->[2]], [$month, $day]) + || !on_or_after([$cur->[1], $cur->[2]], [$month, $day])); + } + return $incr if $incr; } return 0; } sub next_date { - my $self = shift; - my $next = shift; + my $self = shift; + my $next = shift; my $carry = shift; - my @keys = @_; + my @keys = @_; my @cur; my @new; - my @newend; # only used for combined issues + my @newend; # only used for combined issues my $incr; - my $reg = $self->{_mfhdc_REGULARITY}; + my $reg = $self->{_mfhdc_REGULARITY}; my $pattern = $self->{_mfhdc_PATTERN}; - my $freq = $pattern->{w}; + my $freq = $pattern->{w}; foreach my $i (0..$#keys) { - $cur[$i] = $next->{$keys[$i]} if exists $next->{$keys[$i]}; + $cur[$i] = $next->{$keys[$i]} if exists $next->{$keys[$i]}; } # If the current issue has a combined date (eg, May/June) @@ -382,111 +388,111 @@ sub next_date { $cur[-1] =~ s|^[^/]+/||; if (defined $pattern->{y}->{p}) { - # There is a $y publication pattern defined in the record: - # use it to calculate the next issue date. - - foreach my $pubpat (@{$pattern->{y}->{p}}, @{$pattern->{y}->{c}}) { - my $chroncode = substr($pubpat, 0, 1); - my $genfunc = MFHD::Date::generator($chroncode); - my @pats = split(/,/, substr($pubpat, 1)); - - next if $chroncode eq 'e'; - - if (!defined $genfunc) { - carp "Unrecognized chroncode '$chroncode'"; - return undef; - } - - foreach my $pat (@pats) { - my $combined = $pat =~ m|/|; - my ($start, $end); - my @candidate; - -# printf("# next_date: generating with pattern '%s'\n", $pat); - - if ($combined) { - ($start, $end) = split('/', $pat, 2); - } else { - ($start, $end) = (undef, undef); - } - - @candidate = $genfunc->($start || $pat, @cur); - - while ($self->is_omitted(@candidate)) { -# printf("# pubpat omitting date '%s'\n", -# join('/', @candidate)); - @candidate = $genfunc->($start || $pat, @candidate); - } - -# printf("# testing new candidate '%s' against '%s'\n", -# join('/', @candidate), join('/', @new)); - - if (!defined($new[0]) - || !on_or_after(\@candidate, \@new)) { - # first time through the loop - # or @candidate is before @new => - # @candidate is the next issue. - @new = @candidate; - if (defined $end) { - @newend = $genfunc->($end, @cur); - } else { - $newend[0] = undef; - } - -# printf("# selecting candidate date '%s'\n", join('/', @new)); - } - } - } - - if (defined($newend[0])) { - # The best match was a combined issue - foreach my $i (0..$#new) { - # don't combine identical fields - next if $new[$i] eq $newend[$i]; - $new[$i] .= '/' . $newend[$i]; - } - } + # There is a $y publication pattern defined in the record: + # use it to calculate the next issue date. + + foreach my $pubpat (@{$pattern->{y}->{p}}, @{$pattern->{y}->{c}}) { + my $chroncode = substr($pubpat, 0, 1); + my $genfunc = MFHD::Date::generator($chroncode); + my @pats = split(/,/, substr($pubpat, 1)); + + next if $chroncode eq 'e'; + + if (!defined $genfunc) { + carp "Unrecognized chroncode '$chroncode'"; + return undef; + } + + foreach my $pat (@pats) { + my $combined = $pat =~ m|/|; + my ($start, $end); + my @candidate; + + # printf("# next_date: generating with pattern '%s'\n", $pat); + + if ($combined) { + ($start, $end) = split('/', $pat, 2); + } else { + ($start, $end) = (undef, undef); + } + + @candidate = $genfunc->($start || $pat, @cur); + + while ($self->is_omitted(@candidate)) { + # printf("# pubpat omitting date '%s'\n", + # join('/', @candidate)); + @candidate = $genfunc->($start || $pat, @candidate); + } + + # printf("# testing new candidate '%s' against '%s'\n", + # join('/', @candidate), join('/', @new)); + + if ( !defined($new[0]) + || !on_or_after(\@candidate, \@new)) { + # first time through the loop + # or @candidate is before @new => + # @candidate is the next issue. + @new = @candidate; + if (defined $end) { + @newend = $genfunc->($end, @cur); + } else { + $newend[0] = undef; + } + + # printf("# selecting candidate date '%s'\n", join('/', @new)); + } + } + } + + if (defined($newend[0])) { + # The best match was a combined issue + foreach my $i (0..$#new) { + # don't combine identical fields + next if $new[$i] eq $newend[$i]; + $new[$i] .= '/' . $newend[$i]; + } + } } if (scalar @new == 0) { - # There was no suitable publication pattern defined, - # so use the $w frequency to figure out the next date - if (!defined($freq)) { - carp "Undefined frequency in next_date!"; - } elsif (!MFHD::Date::can_increment($freq)) { - carp "Don't know how to deal with frequency '$freq'!"; - } else { - # - # One of the standard defined issue frequencies - # - @new = MFHD::Date::incr_date($freq, @cur); - - while ($self->is_omitted(@new)) { - @new = MFHD::Date::incr_date($freq, @new); - } - - if ($self->is_combined(@new)) { - my @second_date = MFHD::Date::incr_date($freq, @new); - - # I am cheating: This code assumes that only the smallest - # time increment is combined. So, no "Apr 15/May 1" allowed. - $new[-1] = $new[-1] . '/' . $second_date[-1]; - } - } + # There was no suitable publication pattern defined, + # so use the $w frequency to figure out the next date + if (!defined($freq)) { + carp "Undefined frequency in next_date!"; + } elsif (!MFHD::Date::can_increment($freq)) { + carp "Don't know how to deal with frequency '$freq'!"; + } else { + # + # One of the standard defined issue frequencies + # + @new = MFHD::Date::incr_date($freq, @cur); + + while ($self->is_omitted(@new)) { + @new = MFHD::Date::incr_date($freq, @new); + } + + if ($self->is_combined(@new)) { + my @second_date = MFHD::Date::incr_date($freq, @new); + + # I am cheating: This code assumes that only the smallest + # time increment is combined. So, no "Apr 15/May 1" allowed. + $new[-1] = $new[-1] . '/' . $second_date[-1]; + } + } } for my $i (0..$#new) { - $next->{$keys[$i]} = $new[$i]; + $next->{$keys[$i]} = $new[$i]; } # Figure out if we need to adust volume number # right now just use the $carry that was passed in. # in long run, need to base this on ($carry or date_change) if ($carry) { - # if $carry is set, the date doesn't matter: we're not - # going to increment the v. number twice at year-change. - $next->{a} += $carry; + # if $carry is set, the date doesn't matter: we're not + # going to increment the v. number twice at year-change. + $next->{a} += $carry; } elsif (defined $pattern->{x}) { - $next->{a} += $self->calendar_increment(\@cur, \@new); + $next->{a} += $self->calendar_increment(\@cur, \@new); } } @@ -497,21 +503,22 @@ sub next_alt_enum { # First handle any "alternative enumeration", since they're # a lot simpler, and don't depend on the the calendar foreach my $key ('h', 'g') { - next if !exists $next->{$key}; - if (!$self->capstr($key)) { - warn "Holding data exists for $key, but no caption specified"; - $next->{$key} += 1; - last; - } - - my $cap = $self->capfield($key); - if ($cap->{RESTART} && $cap->{COUNT} - && ($next->{$key} == $cap->{COUNT})) { - $next->{$key} = 1; - } else { - $next->{$key} += 1; - last; - } + next if !exists $next->{$key}; + if (!$self->capstr($key)) { + warn "Holding data exists for $key, but no caption specified"; + $next->{$key} += 1; + last; + } + + my $cap = $self->capfield($key); + if ( $cap->{RESTART} + && $cap->{COUNT} + && ($next->{$key} == $cap->{COUNT})) { + $next->{$key} = 1; + } else { + $next->{$key} += 1; + last; + } } } @@ -519,15 +526,15 @@ sub next_alt_enum { # particular publication pattern for the given level of enumeration # returns the pattern string or undef sub enum_pubpat { - my $self = shift; + my $self = shift; my $level = shift; return undef if !exists $self->{_mfhdc_PATTERN}->{y}->{p}; foreach my $reg (@{$self->{_mfhdc_PATTERN}->{y}->{p}}) { - if ($reg =~ m/^e$level/o) { - return substr($reg, 2); - } + if ($reg =~ m/^e$level/o) { + return substr($reg, 2); + } } return undef; } @@ -552,101 +559,102 @@ sub next_enum { # least once. If there's no subfield b, then there's only a single # level of enumeration, so we just add one to it and we're done. if (exists $next->{b}) { - $carry = 0; + $carry = 0; } else { - $carry = 1; + $carry = 1; } foreach my $key (reverse('b'..'f')) { - my $level; - my $pubpat; - - next if !exists $next->{$key}; - - # If the current issue has a combined issue number (eg, 2/3) - # get rid of the first issue number and base the calculation - # on the final issue number in the combined issue. - if ($next->{$key} =~ m|/|) { - $next->{$key} =~ s|^[^/]+/||; - } - - $level = ord($key) - ord('a') + 1; # enumeration level - - $pubpat = $self->enum_pubpat($level); - - if ($pubpat) { -# printf("# next_enum: found pubpat '%s' for subfield '%s'\n", -# $pubpat, $key); - my @pats = split(/,/, $pubpat); - - # If we fall out the bottom of the loop, then $carry - # will still be 1, and we will reset the current - # level to the first value in @pats and increment - # then next higher level. - $carry = 1; - - foreach my $pat (@pats) { - my $combined = $pat =~ m|/|; - my $end; - -# printf("# next_enum: checking current '%s' against pat '%s'\n", -# $next->{$key}, $pat); - - if ($combined) { - ($pat, $end) = split('/', $pat, 2); - } else { - $end = undef; - } - - if ($pat > $next->{$key}) { - $carry = 0; - $next->{$key} = $pat; - $next->{$key} .= '/' . $end if $end; -# printf("# next_enum: selecting new issue no. %s\n", $next->{$key}); - last; # We've found the correct next issue number - } - } - if ($carry) { - $next->{$key} = $pats[0]; - } else { - last; # exit the top level loop because we're done - } - - } else { - # No enumeration publication pattern specified for this level, - # just keed adding one. - - if (!$self->capstr($key)) { - # Just assume that it increments continuously and give up - warn "Holding data exists for $key, but no caption specified"; - $next->{$key} += 1; - $carry = 0; - last; - } - -# printf("# next_enum: no publication pattern, using frequency\n"); - - my $cap = $self->capfield($key); - if ($cap->{RESTART} && $cap->{COUNT} - && ($next->{$key} eq $cap->{COUNT})) { - $next->{$key} = 1; - $carry = 1; - } else { - # If I don't need to "carry" beyond here, then I just increment - # this level of the enumeration and stop looping, since the - # "next" hash has been initialized with the current values - - $next->{$key} += 1; - $carry = 0; - } - - # You can't have a combined issue that spans two volumes: no.12/1 - # is forbidden - if ($self->enum_is_combined($key, $next->{$key})) { - $next->{$key} .= '/' . ($next->{$key} + 1); - } - - last if !$carry; - } + my $level; + my $pubpat; + + next if !exists $next->{$key}; + + # If the current issue has a combined issue number (eg, 2/3) + # get rid of the first issue number and base the calculation + # on the final issue number in the combined issue. + if ($next->{$key} =~ m|/|) { + $next->{$key} =~ s|^[^/]+/||; + } + + $level = ord($key) - ord('a') + 1; # enumeration level + + $pubpat = $self->enum_pubpat($level); + + if ($pubpat) { + # printf("# next_enum: found pubpat '%s' for subfield '%s'\n", + # $pubpat, $key); + my @pats = split(/,/, $pubpat); + + # If we fall out the bottom of the loop, then $carry + # will still be 1, and we will reset the current + # level to the first value in @pats and increment + # then next higher level. + $carry = 1; + + foreach my $pat (@pats) { + my $combined = $pat =~ m|/|; + my $end; + + # printf("# next_enum: checking current '%s' against pat '%s'\n", + # $next->{$key}, $pat); + + if ($combined) { + ($pat, $end) = split('/', $pat, 2); + } else { + $end = undef; + } + + if ($pat > $next->{$key}) { + $carry = 0; + $next->{$key} = $pat; + $next->{$key} .= '/' . $end if $end; + # printf("# next_enum: selecting new issue no. %s\n", $next->{$key}); + last; # We've found the correct next issue number + } + } + if ($carry) { + $next->{$key} = $pats[0]; + } else { + last; # exit the top level loop because we're done + } + + } else { + # No enumeration publication pattern specified for this level, + # just keed adding one. + + if (!$self->capstr($key)) { + # Just assume that it increments continuously and give up + warn "Holding data exists for $key, but no caption specified"; + $next->{$key} += 1; + $carry = 0; + last; + } + + # printf("# next_enum: no publication pattern, using frequency\n"); + + my $cap = $self->capfield($key); + if ( $cap->{RESTART} + && $cap->{COUNT} + && ($next->{$key} eq $cap->{COUNT})) { + $next->{$key} = 1; + $carry = 1; + } else { + # If I don't need to "carry" beyond here, then I just increment + # this level of the enumeration and stop looping, since the + # "next" hash has been initialized with the current values + + $next->{$key} += 1; + $carry = 0; + } + + # You can't have a combined issue that spans two volumes: no.12/1 + # is forbidden + if ($self->enum_is_combined($key, $next->{$key})) { + $next->{$key} .= '/' . ($next->{$key} + 1); + } + + last if !$carry; + } } # The easy part is done. There are two things left to do: @@ -655,51 +663,51 @@ sub next_enum { # or because $carry is set because of the above loop if (!$self->subfield('i')) { - # The simple case: if there is no chronology specified - # then just check $carry and return - $next->{'a'} += $carry; + # The simple case: if there is no chronology specified + # then just check $carry and return + $next->{'a'} += $carry; } else { - # Figure out date of next issue, then decide if we need - # to adjust top level enumeration based on that - $self->next_date($next, $carry, ('i'..'m')); + # Figure out date of next issue, then decide if we need + # to adjust top level enumeration based on that + $self->next_date($next, $carry, ('i'..'m')); } } sub next { - my $self = shift; + my $self = shift; my $holding = shift; - my $next = {}; + my $next = {}; # Initialize $next with current enumeration & chronology, then # we can just operate on $next, based on the contents of the caption if ($self->enumeration_is_chronology) { - foreach my $key ('a' .. 'h') { - $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key} - if defined $holding->{_mfhdh_SUBFIELDS}->{$key}; - } - $self->next_date($next, 0, ('a' .. 'h')); + foreach my $key ('a'..'h') { + $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key} + if defined $holding->{_mfhdh_SUBFIELDS}->{$key}; + } + $self->next_date($next, 0, ('a'..'h')); - return $next; + return $next; } - foreach my $key ('a' .. 'h') { - $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS} - if defined $holding->{_mfhdh_SUBFIELDS}->{$key}; + foreach my $key ('a'..'h') { + $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS} + if defined $holding->{_mfhdh_SUBFIELDS}->{$key}; } foreach my $key ('i'..'m') { - $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key} - if defined $holding->{_mfhdh_SUBFIELDS}->{$key}; + $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key} + if defined $holding->{_mfhdh_SUBFIELDS}->{$key}; } if (exists $next->{'h'}) { - $self->next_alt_enum($next); + $self->next_alt_enum($next); } $self->next_enum($next); - return($next); + return ($next); } 1; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Date.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Date.pm index 81aee665b3..65b9695f96 100644 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Date.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Date.pm @@ -8,22 +8,22 @@ use DateTime; use base 'Exporter'; -our @EXPORT_OK =qw(dispatch generator incr_date can_increment); +our @EXPORT_OK = qw(dispatch generator incr_date can_increment); my %daynames = ( - 'mo' => 1, - 'tu' => 2, - 'we' => 3, - 'th' => 4, - 'fr' => 5, - 'sa' => 6, - 'su' => 7, - ); - -my $daypat = '(mo|tu|we|th|fr|sa|su)'; + 'mo' => 1, + 'tu' => 2, + 'we' => 3, + 'th' => 4, + 'fr' => 5, + 'sa' => 6, + 'su' => 7, +); + +my $daypat = '(mo|tu|we|th|fr|sa|su)'; my $weekpat = '(99|98|97|00|01|02|03|04|05)'; my $weeknopat; -my $monthpat = '(01|02|03|04|05|06|07|08|09|10|11|12)'; +my $monthpat = '(01|02|03|04|05|06|07|08|09|10|11|12)'; my $seasonpat = '(21|22|23|24)'; # Initialize $weeknopat to be '(01|02|03|...|51|52|53)' @@ -34,113 +34,118 @@ foreach my $weekno (1..52) { $weeknopat .= '53)'; sub match_day { - my $pat = shift; + my $pat = shift; my @date = @_; # Translate daynames into day of week for DateTime # also used to check if dayname is valid. if (exists $daynames{$pat}) { - # dd - # figure out day of week for date and compare - my $dt = DateTime->new(year => $date[0], - month => $date[1], - day => $date[2]); - return ($dt->day_of_week == $daynames{$pat}); + # dd + # figure out day of week for date and compare + my $dt = DateTime->new( + year => $date[0], + month => $date[1], + day => $date[2] + ); + return ($dt->day_of_week == $daynames{$pat}); } elsif (length($pat) == 2) { - # DD - return $pat == $date[2]; + # DD + return $pat == $date[2]; } elsif (length($pat) == 4) { - # MMDD - my ($mon, $day) = unpack("a2a2", $pat); + # MMDD + my ($mon, $day) = unpack("a2a2", $pat); - return (($mon == $date[1]) && ($day == $date[2])); + return (($mon == $date[1]) && ($day == $date[2])); } else { - carp "Invalid day pattern '$pat'"; - return 0; + carp "Invalid day pattern '$pat'"; + return 0; } } sub subsequent_day { my $pat = shift; my @cur = @_; - my $dt = DateTime->new(year => $cur[0], - month => $cur[1], - day => $cur[2]); + my $dt = DateTime->new( + year => $cur[0], + month => $cur[1], + day => $cur[2] + ); -# printf("# subsequent_day: pat='%s' cur='%s'\n", $pat, join('/', @cur)); + # printf("# subsequent_day: pat='%s' cur='%s'\n", $pat, join('/', @cur)); if (exists $daynames{$pat}) { - # dd: published on the given weekday - my $dow = $dt->day_of_week; - my $corr = ($daynames{$pat} - $dow + 7) % 7; - - if ($dow == $daynames{$pat}) { - # the next one is one week hence - $dt->add(days => 7); - } else { - # the next one is later this week, - # or it is next week (ie, on or after next Monday) - # $corr will take care of it. - $dt->add(days => $corr); - } - @cur = ($dt->year, $dt->month, $dt->day); + # dd: published on the given weekday + my $dow = $dt->day_of_week; + my $corr = ($daynames{$pat} - $dow + 7) % 7; + + if ($dow == $daynames{$pat}) { + # the next one is one week hence + $dt->add(days => 7); + } else { + # the next one is later this week, + # or it is next week (ie, on or after next Monday) + # $corr will take care of it. + $dt->add(days => $corr); + } + @cur = ($dt->year, $dt->month, $dt->day); } elsif (length($pat) == 2) { - # DD: published on the give day of every month - if ($dt->day >= $pat) { - # current date is on or after $pat: next one is next month - $dt->set(day => $pat); - $dt->add(months => 1); - @cur = ($dt->year, $dt->month, $dt->day); - } else { - # current date is before $pat: set day to pattern - $cur[2] = $pat; - } + # DD: published on the give day of every month + if ($dt->day >= $pat) { + # current date is on or after $pat: next one is next month + $dt->set(day => $pat); + $dt->add(months => 1); + @cur = ($dt->year, $dt->month, $dt->day); + } else { + # current date is before $pat: set day to pattern + $cur[2] = $pat; + } } elsif (length($pat) == 4) { - # MMDD: published on the given day of the given month - my ($mon, $day) = unpack("a2a2", $pat); - - if (on_or_after($mon, $day, $cur[1], $cur[2])) { - # Current date is on or after pattern; next one is next year - $cur[0] += 1; - } - # Year is now right. Either it's next year (because of on_or_after) - # or it's this year, because the current date is NOT on or after - # the pattern. Just fix the month and day - $cur[1] = $mon; - $cur[2] = $day; + # MMDD: published on the given day of the given month + my ($mon, $day) = unpack("a2a2", $pat); + + if (on_or_after($mon, $day, $cur[1], $cur[2])) { + # Current date is on or after pattern; next one is next year + $cur[0] += 1; + } + # Year is now right. Either it's next year (because of on_or_after) + # or it's this year, because the current date is NOT on or after + # the pattern. Just fix the month and day + $cur[1] = $mon; + $cur[2] = $day; } else { - carp "Invalid day pattern '$pat'"; - return undef; + carp "Invalid day pattern '$pat'"; + return undef; } foreach my $i (0..$#cur) { - $cur[$i] = '0' . (0+$cur[$i]) if $cur[$i] < 10; + $cur[$i] = '0' . (0 + $cur[$i]) if $cur[$i] < 10; } -# printf("subsequent_day: returning '%s'\n", join('/', @cur)); + # printf("subsequent_day: returning '%s'\n", join('/', @cur)); return @cur; } - # Calculate date of 3rd Friday of the month (for example) # 1-5: count from beginning of month # 99-97: count back from end of month sub nth_week_of_month { - my $dt = shift; + my $dt = shift; my $week = shift; - my $day = shift; + my $day = shift; my ($nth_day, $dow); -# printf("# nth_week_of_month(dt, '%s', '%s')\n", $week, $day); + # printf("# nth_week_of_month(dt, '%s', '%s')\n", $week, $day); if (0 < $week && $week <= 5) { - $nth_day = $dt->clone->set(day => 1); + $nth_day = $dt->clone->set(day => 1); } elsif ($week >= 97) { - $nth_day = DateTime->last_day_of_month(year => $dt->year, - month => $dt->month); + $nth_day = DateTime->last_day_of_month( + year => $dt->year, + month => $dt->month + ); } else { - return undef; + return undef; } $dow = $nth_day->day_of_week(); @@ -149,23 +154,25 @@ sub nth_week_of_month { # then use that day for the calculations, otherwise, just use # the day of the week of the original date (the date $dt). if (defined($day)) { - $day = $daynames{$day}; + $day = $daynames{$day}; } else { - $day = $dt->day_of_week; + $day = $dt->day_of_week; } if ($week <= 5) { - # count forwards - $nth_day->add(days => ($day - $dow + 7) % 7, - weeks=> $week - 1); + # count forwards + $nth_day->add( + days => ($day - $dow + 7) % 7, + weeks => $week - 1 + ); } else { - # count backwards - $nth_day->subtract(days => ($day - $dow + 7) % 7); + # count backwards + $nth_day->subtract(days => ($day - $dow + 7) % 7); - # 99: last week of month, 98: second last, etc. - for (my $i = 99 - $week; $i > 0; $i--) { - $nth_day->subtract(weeks => 1); - } + # 99: last week of month, 98: second last, etc. + for (my $i = 99 - $week; $i > 0; $i--) { + $nth_day->subtract(weeks => 1); + } } # There is no nth "day" in the month! @@ -179,74 +186,92 @@ sub nth_week_of_month { # of month, week, and day # sub check_date { - my $dt = shift; - my $month = shift; + my $dt = shift; + my $month = shift; my $weekno = shift; - my $day = shift; + my $day = shift; -# printf("check_date('%s', '%s', '%s')\n", $month, $weekno, $day || ''); + # printf("check_date('%s', '%s', '%s')\n", $month, $weekno, $day || ''); if (!defined $day) { - # MMWW - return (($dt->month == $month) - && (($dt->week_of_month == $weekno) - || ($weekno >= 97 - && ($dt->week_of_month == nth_week_of_month($dt, $weekno, $day)->week_of_month)))); + # MMWW + return ( + ($dt->month == $month) + && ( + ($dt->week_of_month == $weekno) + || ( + $weekno >= 97 + && ($dt->week_of_month == + nth_week_of_month($dt, $weekno, $day)->week_of_month) + ) + ) + ); } # simple cases first if ($daynames{$day} != $dt->day_of_week) { - # if it's the wrong day of the week, rest doesn't matter - return 0; + # if it's the wrong day of the week, rest doesn't matter + return 0; } if (!defined $month) { - # WWdd - return (($weekno == 0) # Every week - || ($dt->weekday_of_month == $weekno) # this week - || (($weekno >= 97) && ($dt->weekday_of_month == nth_week_of_month($dt, $weekno, $day)->weekday_of_month))); + # WWdd + return ( + ($weekno == 0) # Every week + || ($dt->weekday_of_month == $weekno) # this week + || ( + ($weekno >= 97) + && ($dt->weekday_of_month == + nth_week_of_month($dt, $weekno, $day)->weekday_of_month) + ) + ); } # MMWWdd if ($month != $dt->month) { - # If it's the wrong month, then we're done - return 0; + # If it's the wrong month, then we're done + return 0; } # It's the right day of the week # It's the right month - if (($weekno == 0) ||($weekno == $dt->weekday_of_month)) { - # If this matches, then we're counting from the beginning - # of the month and it matches and we're done. - return 1; + if (($weekno == 0) || ($weekno == $dt->weekday_of_month)) { + # If this matches, then we're counting from the beginning + # of the month and it matches and we're done. + return 1; } # only case left is that the week number is counting from # the end of the month: eg, second last wednesday - return (($weekno >= 97) - && (nth_week_of_month($dt, $weekno, $day)->weekday_of_month == $dt->weekday_of_month)); + return ( + ($weekno >= 97) + && (nth_week_of_month($dt, $weekno, $day)->weekday_of_month == + $dt->weekday_of_month) + ); } sub match_week { - my $pat = shift; + my $pat = shift; my @date = @_; - my $dt = DateTime->new(year => $date[0], - month => $date[1], - day => $date[2]); + my $dt = DateTime->new( + year => $date[0], + month => $date[1], + day => $date[2] + ); if ($pat =~ m/^$weekpat$daypat$/) { - # WWdd: 03we = Third Wednesday - return check_date($dt, undef, $1, $2); + # WWdd: 03we = Third Wednesday + return check_date($dt, undef, $1, $2); } elsif ($pat =~ m/^$monthpat$weekpat$daypat$/) { - # MMWWdd: 0599tu Last Tuesday in May XXX WRITE ME - return check_date($dt, $1, $2, $3); + # MMWWdd: 0599tu Last Tuesday in May XXX WRITE ME + return check_date($dt, $1, $2, $3); } elsif ($pat =~ m/^$monthpat$weekpat$/) { - # MMWW: 1204: Fourth week in December XXX WRITE ME - return check_date($dt, $1, $2, undef); + # MMWW: 1204: Fourth week in December XXX WRITE ME + return check_date($dt, $1, $2, undef); } else { - carp "invalid week pattern '$pat'"; - return 0; + carp "invalid week pattern '$pat'"; + return 0; } } @@ -259,80 +284,88 @@ sub subsequent_week { my $candidate; my $dt; -# printf("# subsequent_week('%s', '%s', '%s', '%s')\n", $pat, @cur); + # printf("# subsequent_week('%s', '%s', '%s', '%s')\n", $pat, @cur); - $dt = DateTime->new(year => $cur[0], - month=> $cur[1], - day => $cur[2]); + $dt = DateTime->new( + year => $cur[0], + month => $cur[1], + day => $cur[2] + ); if ($pat =~ m/^$weekpat$daypat$/o) { - # WWdd: published on given weekday of given week of every month - my ($week, $day) = ($1, $2); - -# printf("# subsequent_week: matched /WWdd/: week='%s', day='%s'\n", -# $week, $day); - - if ($week eq '00') { - # Every week - $candidate = $dt->clone; - - if ($dt->day_of_week == $daynames{$day}) { - # Current is right day, next one is a week hence - $candidate->add(days => 7); - } else { - $candidate->add(days => ($daynames{$day} - $dt->day_of_week + 7) % 7); - } - } else { - # 3rd Friday of the month (eg) - $candidate = nth_week_of_month($dt, $week, $day); - } - - if ($candidate <= $dt) { - # If the n'th week of the month happens on before the - # current issue, then the next issue is published next - # month, otherwise, it's published this month. - # This will never happen for the "00: every week" pattern + # WWdd: published on given weekday of given week of every month + my ($week, $day) = ($1, $2); + + # printf("# subsequent_week: matched /WWdd/: week='%s', day='%s'\n", + # $week, $day); + + if ($week eq '00') { + # Every week + $candidate = $dt->clone; + + if ($dt->day_of_week == $daynames{$day}) { + # Current is right day, next one is a week hence + $candidate->add(days => 7); + } else { + $candidate->add( + days => ($daynames{$day} - $dt->day_of_week + 7) % 7); + } + } else { + # 3rd Friday of the month (eg) + $candidate = nth_week_of_month($dt, $week, $day); + } + + if ($candidate <= $dt) { +# If the n'th week of the month happens on before the +# current issue, then the next issue is published next +# month, otherwise, it's published this month. +# This will never happen for the "00: every week" pattern # printf("# subsequent_week: candidate (%s) occurs on or before current date (%s)\n", # join('/', $candidate->year, $candidate->month, $candidate->day), # join('/', $dt->year, $dt->month, $dt->day)); - $candidate->set(day => 1); - $candidate->add(months => 1); - $candidate = nth_week_of_month($candidate, $week, $day); - } + $candidate->set(day => 1); + $candidate->add(months => 1); + $candidate = nth_week_of_month($candidate, $week, $day); + } } elsif ($pat =~ m/^$monthpat$weekpat$daypat$/) { - # MMWWdd: published on given weekday of given week of given month - my ($month, $week, $day) = ($1, $2, $3); + # MMWWdd: published on given weekday of given week of given month + my ($month, $week, $day) = ($1, $2, $3); # printf("# subsequent_week: matched /MMWWdd/: month='%s', week='%s', day='%s'\n", # $month, $week, $day); - $candidate = DateTime->new(year => $dt->year, - month=> $month, - day => 1); - $candidate = nth_week_of_month($candidate, $week, $day); - if ($candidate <= $dt) { - # We've missed it for this year, next one that matches - # will be next year - $candidate->add(years => 1)->set(day => 1); - $candidate = nth_week_of_month($candidate, $week, $day); - } + $candidate = DateTime->new( + year => $dt->year, + month => $month, + day => 1 + ); + $candidate = nth_week_of_month($candidate, $week, $day); + if ($candidate <= $dt) { + # We've missed it for this year, next one that matches + # will be next year + $candidate->add(years => 1)->set(day => 1); + $candidate = nth_week_of_month($candidate, $week, $day); + } } elsif ($pat =~ m/^$monthpat$weekpat$/) { - # MMWW: published during given week of given month - my ($month, $week) = ($1, $2); - - $candidate = nth_week_of_month(DateTime->new(year => $dt->year, - month=> $month, - day => 1), - $week, - 'th'); - if ($candidate <= $dt) { - # Already past the pattern date this year, move to next year - $candidate->add(years => 1)->set(day => 1); - $candidate = nth_week_of_month($candidate, $week, 'th'); - } + # MMWW: published during given week of given month + my ($month, $week) = ($1, $2); + + $candidate = nth_week_of_month( + DateTime->new( + year => $dt->year, + month => $month, + day => 1 + ), + $week, 'th' + ); + if ($candidate <= $dt) { + # Already past the pattern date this year, move to next year + $candidate->add(years => 1)->set(day => 1); + $candidate = nth_week_of_month($candidate, $week, 'th'); + } } else { - carp "invalid week pattern '$pat'"; - return undef; + carp "invalid week pattern '$pat'"; + return undef; } $cur[0] = $candidate->year; @@ -340,14 +373,14 @@ sub subsequent_week { $cur[2] = $candidate->day; foreach my $i (0..$#cur) { - $cur[$i] = '0' . (0+$cur[$i]) if $cur[$i] < 10; + $cur[$i] = '0' . (0 + $cur[$i]) if $cur[$i] < 10; } return @cur; } sub match_month { - my $pat = shift; + my $pat = shift; my @date = @_; return ($pat eq $date[1]); @@ -358,9 +391,9 @@ sub subsequent_month { my @cur = @_; if ($cur[1] >= $pat) { - # Current date is on or after the patter date, so the next - # occurence is next year - $cur[0] += 1; + # Current date is on or after the patter date, so the next + # occurence is next year + $cur[0] += 1; } # The year is right, just set the month to the pattern date. @@ -370,7 +403,7 @@ sub subsequent_month { } sub match_season { - my $pat = shift; + my $pat = shift; my @date = @_; return ($pat eq $date[1]); @@ -383,14 +416,14 @@ sub subsequent_season { # printf("# subsequent_season: pat='%s', cur='%s'\n", $pat, join('/',@cur)); if (($pat < 21) || ($pat > 24)) { - carp "Unexpected season '$pat'"; - return undef; + carp "Unexpected season '$pat'"; + return undef; } if ($cur[1] >= $pat) { - # current season is on or past pattern season in this year, - # advance to next year - $cur[0] += 1; + # current season is on or past pattern season in this year, + # advance to next year + $cur[0] += 1; } # Either we've advanced to the next year or the current season # is before the pattern season in the current year. Either way, @@ -401,7 +434,7 @@ sub subsequent_season { } sub match_year { - my $pat = shift; + my $pat = shift; my @date = @_; # XXX WRITE ME @@ -417,7 +450,7 @@ sub subsequent_year { } sub match_issue { - my $pat = shift; + my $pat = shift; my @date = @_; # We handle enumeration patterns separately. This just @@ -435,21 +468,21 @@ sub subsequent_issue { } my %dispatch = ( - d => \&match_day, - e => \&match_issue, # not really a "chron" code - w => \&match_week, - m => \&match_month, - s => \&match_season, - y => \&match_year, + d => \&match_day, + e => \&match_issue, # not really a "chron" code + w => \&match_week, + m => \&match_month, + s => \&match_season, + y => \&match_year, ); my %generators = ( - d => \&subsequent_day, - e => \&subsequent_issue, # not really a "chron" code - w => \&subsequent_week, - m => \&subsequent_month, - s => \&subsequent_season, - y => \&subsequent_year, + d => \&subsequent_day, + e => \&subsequent_issue, # not really a "chron" code + w => \&subsequent_week, + m => \&subsequent_month, + s => \&subsequent_season, + y => \&subsequent_year, ); sub dispatch { @@ -465,23 +498,23 @@ sub generator { } my %increments = ( - a => {years => 1}, # annual - b => {months => 2}, # bimonthly - c => {days => 3}, # semiweekly - d => {days => 1}, # daily - e => {weeks => 2}, # biweekly - f => {months => 6}, # semiannual - g => {years => 2}, # biennial - h => {years => 3}, # triennial - i => {days => 2}, # three times / week - j => {days => 10}, # three times /month - # k => continuous - m => {months => 1}, # monthly - q => {months => 3}, # quarterly - s => {days => 15}, # semimonthly - t => {months => 4}, # three times / year - w => {weeks => 1}, # weekly - # x => completely irregular + a => {years => 1}, # annual + b => {months => 2}, # bimonthly + c => {days => 3}, # semiweekly + d => {days => 1}, # daily + e => {weeks => 2}, # biweekly + f => {months => 6}, # semiannual + g => {years => 2}, # biennial + h => {years => 3}, # triennial + i => {days => 2}, # three times / week + j => {days => 10}, # three times /month + # k => continuous + m => {months => 1}, # monthly + q => {months => 3}, # quarterly + s => {days => 15}, # semimonthly + t => {months => 4}, # three times / year + w => {weeks => 1}, # weekly + # x => completely irregular ); sub can_increment { @@ -493,49 +526,51 @@ sub can_increment { sub incr_date { my $freq = shift; my $incr = $increments{$freq}; - my @new = @_; + my @new = @_; if (scalar(@new) == 1) { - # only a year is specified. Next date is easy - $new[0] += $incr->{years} || 1; + # only a year is specified. Next date is easy + $new[0] += $incr->{years} || 1; } elsif (scalar(@new) == 2) { - # Year and month or season - if ($new[1] > 20) { - # season - $new[1] += ($incr->{months}/3) || 1; - if ($new[1] > 24) { - # carry - $new[0] += 1; - $new[1] -= 4; # 25 - 4 == 21 == Spring after Winter - } - } else { - # month - $new[1] += $incr->{months} || 1; - if ($new[1] > 12) { - # carry - $new[0] += 1; - $new[1] -= 12; - } - } + # Year and month or season + if ($new[1] > 20) { + # season + $new[1] += ($incr->{months} / 3) || 1; + if ($new[1] > 24) { + # carry + $new[0] += 1; + $new[1] -= 4; # 25 - 4 == 21 == Spring after Winter + } + } else { + # month + $new[1] += $incr->{months} || 1; + if ($new[1] > 12) { + # carry + $new[0] += 1; + $new[1] -= 12; + } + } } elsif (scalar(@new) == 3) { - # Year, Month, Day: now it gets complicated. - - if ($new[2] =~ /^[0-9]+$/) { - # A single number for the day of month, relatively simple - my $dt = DateTime->new(year => $new[0], - month=> $new[1], - day => $new[2]); - $dt->add(%{$incr}); - $new[0] = $dt->year; - $new[1] = $dt->month; - $new[2] = $dt->day; - } + # Year, Month, Day: now it gets complicated. + + if ($new[2] =~ /^[0-9]+$/) { + # A single number for the day of month, relatively simple + my $dt = DateTime->new( + year => $new[0], + month => $new[1], + day => $new[2] + ); + $dt->add(%{$incr}); + $new[0] = $dt->year; + $new[1] = $dt->month; + $new[2] = $dt->day; + } } else { - warn("Don't know how to cope with @new"); + warn("Don't know how to cope with @new"); } foreach my $i (0..$#new) { - $new[$i] = '0' . (0+$new[$i]) if $new[$i] < 10; + $new[$i] = '0' . (0 + $new[$i]) if $new[$i] < 10; } return @new; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm index a656e60326..4d6f61e75a 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm @@ -10,51 +10,54 @@ use Data::Dumper; use base 'MARC::Field'; sub new { - my $proto = shift; - my $class = ref($proto) || $proto; - my $seqno = shift; - my $self = shift; - my $caption = shift; + my $proto = shift; + my $class = ref($proto) || $proto; + my $seqno = shift; + my $self = shift; + my $caption = shift; my $last_enum = undef; - $self->{_mfhdh_SEQNO} = $seqno; - $self->{_mfhdh_CAPTION} = $caption; - $self->{_mfhdh_DESCR} = {}; - $self->{_mfhdh_COPY} = undef; - $self->{_mfhdh_BREAK} = undef; - $self->{_mfhdh_NOTES} = {}; + $self->{_mfhdh_SEQNO} = $seqno; + $self->{_mfhdh_CAPTION} = $caption; + $self->{_mfhdh_DESCR} = {}; + $self->{_mfhdh_COPY} = undef; + $self->{_mfhdh_BREAK} = undef; + $self->{_mfhdh_NOTES} = {}; $self->{_mfhdh_COPYRIGHT} = []; foreach my $subfield ($self->subfields) { - my ($key, $val) = @$subfield; - - if (($caption && $caption->enumeration_is_chronology && $key =~ /[a-h]/) || $key =~ /[i-m]/) { - # Chronology - $self->{_mfhdh_SUBFIELDS}->{$key} = $val; - } elsif ($key =~ /[a-h]/) { - # Enumeration details of holdings - $self->{_mfhdh_SUBFIELDS}->{$key} = {HOLDINGS => $val, - UNIT => undef,}; - $last_enum = $key; - } elsif ($key eq 'o') { - warn '$o specified prior to first enumeration' - unless defined($last_enum); - $self->{_mfhdh_SUBFIELDS}->{$last_enum}->{UNIT} = $val; - $last_enum = undef; - } elsif ($key =~ /[npq]/) { - $self->{_mfhdh_DESCR}->{$key} = $val; - } elsif ($key eq 's') { - push @{$self->{_mfhdh_COPYRIGHT}}, $val; - } elsif ($key eq 't') { - $self->{_mfhdh_COPY} = $val; - } elsif ($key eq 'w') { - carp "Unrecognized break indicator '$val'" - unless $val =~ /^[gn]$/; - $self->{_mfhdh_BREAK} = $val; - } + my ($key, $val) = @$subfield; + + if (($caption && $caption->enumeration_is_chronology && $key =~ /[a-h]/) + || $key =~ /[i-m]/) { + # Chronology + $self->{_mfhdh_SUBFIELDS}->{$key} = $val; + } elsif ($key =~ /[a-h]/) { + # Enumeration details of holdings + $self->{_mfhdh_SUBFIELDS}->{$key} = { + HOLDINGS => $val, + UNIT => undef, + }; + $last_enum = $key; + } elsif ($key eq 'o') { + warn '$o specified prior to first enumeration' + unless defined($last_enum); + $self->{_mfhdh_SUBFIELDS}->{$last_enum}->{UNIT} = $val; + $last_enum = undef; + } elsif ($key =~ /[npq]/) { + $self->{_mfhdh_DESCR}->{$key} = $val; + } elsif ($key eq 's') { + push @{$self->{_mfhdh_COPYRIGHT}}, $val; + } elsif ($key eq 't') { + $self->{_mfhdh_COPY} = $val; + } elsif ($key eq 'w') { + carp "Unrecognized break indicator '$val'" + unless $val =~ /^[gn]$/; + $self->{_mfhdh_BREAK} = $val; + } } - bless ($self, $class); + bless($self, $class); return $self; } @@ -71,109 +74,128 @@ sub caption { } sub format_chron { - my $self = shift; + my $self = shift; my $caption = $self->{_mfhdh_CAPTION}; my @keys; - my $str = ''; - my %month = ( '01' => 'Jan.', '02' => 'Feb.', '03' => 'Mar.', - '04' => 'Apr.', '05' => 'May ', '06' => 'Jun.', - '07' => 'Jul.', '08' => 'Aug.', '09' => 'Sep.', - '10' => 'Oct.', '11' => 'Nov.', '12' => 'Dec.', - '21' => 'Spring', '22' => 'Summer', - '23' => 'Autumn', '24' => 'Winter' ); + my $str = ''; + my %month = ( + '01' => 'Jan.', + '02' => 'Feb.', + '03' => 'Mar.', + '04' => 'Apr.', + '05' => 'May ', + '06' => 'Jun.', + '07' => 'Jul.', + '08' => 'Aug.', + '09' => 'Sep.', + '10' => 'Oct.', + '11' => 'Nov.', + '12' => 'Dec.', + '21' => 'Spring', + '22' => 'Summer', + '23' => 'Autumn', + '24' => 'Winter' + ); @keys = @_; - foreach my $i (0 .. @keys) { - my $key = $keys[$i]; - my $capstr; - my $chron; - my $sep; - - last if !defined $caption->capstr($key); - - $capstr = $caption->capstr($key); - if (substr($capstr,0,1) eq '(') { - # a caption enclosed in parentheses is not displayed - $capstr = ''; - } - - # If this is the second level of chronology, then it's - # likely to be a month or season, so we should use the - # string name rather than the number given. - if (($i == 1) && exists $month{$self->{_mfhdh_SUBFIELDS}->{$key}}) { - $chron = $month{$self->{_mfhdh_SUBFIELDS}->{$key}}; - } else { - $chron = $self->{_mfhdh_SUBFIELDS}->{$key}; - } - - - $str .= (($i == 0 || $str =~ /[. ]$/) ? '' : ':') . $capstr . $chron; + foreach my $i (0..@keys) { + my $key = $keys[$i]; + my $capstr; + my $chron; + my $sep; + + last if !defined $caption->capstr($key); + + $capstr = $caption->capstr($key); + if (substr($capstr, 0, 1) eq '(') { + # a caption enclosed in parentheses is not displayed + $capstr = ''; + } + + # If this is the second level of chronology, then it's + # likely to be a month or season, so we should use the + # string name rather than the number given. + if (($i == 1) && exists $month{$self->{_mfhdh_SUBFIELDS}->{$key}}) { + $chron = $month{$self->{_mfhdh_SUBFIELDS}->{$key}}; + } else { + $chron = $self->{_mfhdh_SUBFIELDS}->{$key}; + } + + $str .= (($i == 0 || $str =~ /[. ]$/) ? '' : ':') . $capstr . $chron; } return $str; } sub format { - my $self = shift; + my $self = shift; my $caption = $self->{_mfhdh_CAPTION}; - my $str = ''; + my $str = ''; if ($caption->type_of_unit) { - $str = $caption->type_of_unit . ' '; + $str = $caption->type_of_unit . ' '; } if ($caption->enumeration_is_chronology) { - # if issues are identified by chronology only, then the - # chronology data is stored in the enumeration subfields, - # so format those fields as if they were chronological. - $str = $self->format_chron('a'..'f'); + # if issues are identified by chronology only, then the + # chronology data is stored in the enumeration subfields, + # so format those fields as if they were chronological. + $str = $self->format_chron('a'..'f'); } else { - # OK, there is enumeration data and maybe chronology - # data as well, format both parts appropriately - - # Enumerations - foreach my $key ('a'..'f') { - my $capstr; - my $chron; - my $sep; - - last if !defined $caption->capstr($key); - - $capstr = $caption->capstr($key); - if (substr($capstr, 0, 1) eq '(') { - # a caption enclosed in parentheses is not displayed - $capstr = ''; - } - $str .= ($key eq 'a' ? '' : ':') . $capstr . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS}; - } - - # Chronology - if (defined $caption->capstr('i')) { - $str .= '('; - $str .= $self->format_chron('i'..'l'); - $str .= ')'; - } - - if ($caption->capstr('g')) { - # There's at least one level of alternative enumeration - $str .= '='; - foreach my $key ('g', 'h') { - $str .= ($key eq 'g' ? '' : ':') . $caption->capstr($key) . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS}; - } - - # This assumes that alternative chronology is only ever - # provided if there is an alternative enumeration. - if ($caption->capstr('m')) { - # Alternative Chronology - $str .= '('; - $str .= $caption->capstr('m') . $self->{_mfhdh_SUBFIELDS}->{m}->{HOLDINGS}; - $str .= ')'; - } - } + # OK, there is enumeration data and maybe chronology + # data as well, format both parts appropriately + + # Enumerations + foreach my $key ('a'..'f') { + my $capstr; + my $chron; + my $sep; + + last if !defined $caption->capstr($key); + + $capstr = $caption->capstr($key); + if (substr($capstr, 0, 1) eq '(') { + # a caption enclosed in parentheses is not displayed + $capstr = ''; + } + $str .= + ($key eq 'a' ? '' : ':') + . $capstr + . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS}; + } + + # Chronology + if (defined $caption->capstr('i')) { + $str .= '('; + $str .= $self->format_chron('i'..'l'); + $str .= ')'; + } + + if ($caption->capstr('g')) { + # There's at least one level of alternative enumeration + $str .= '='; + foreach my $key ('g', 'h') { + $str .= + ($key eq 'g' ? '' : ':') + . $caption->capstr($key) + . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS}; + } + + # This assumes that alternative chronology is only ever + # provided if there is an alternative enumeration. + if ($caption->capstr('m')) { + # Alternative Chronology + $str .= '('; + $str .= + $caption->capstr('m') + . $self->{_mfhdh_SUBFIELDS}->{m}->{HOLDINGS}; + $str .= ')'; + } + } } # Public Note - $str .= ' '. $caption->capstr('z') if (defined $caption->capstr('z')); + $str .= ' ' . $caption->capstr('z') if (defined $caption->capstr('z')); # Breaks in the sequence if (defined($self->{_mfhdh_BREAK})) { @@ -189,13 +211,12 @@ sub format { return $str; } - # next: Given a holding statement, return a hash containing the # enumeration values for the next issues, whether we hold it or not # Just pass through to Caption::next # sub next { - my $self = shift; + my $self = shift; my $caption = $self->{_mfhdh_CAPTION}; return $caption->next($self); @@ -208,42 +229,47 @@ sub next { # # sub match { - my $self = shift; - my $pat = shift; + my $self = shift; + my $pat = shift; my $caption = $self->{_mfhdh_CAPTION}; foreach my $key ('a'..'f') { - my $nextkey; - - ($nextkey = $key)++; - # If the next smaller enumeration exists, and is numbered - # continuously, then we don't need to check this one, because - # gaps in issue numbering matter, not changes in volume numbering - next if (exists $self->{_mfhdh_SUBFIELDS}->{$nextkey} - && !$caption->capfield($nextkey)->{RESTART}); - - # If a subfield exists in $self but not in $pat, or vice versa - # or if the field has different values, then fail - if (exists($self->{_mfhdh_SUBFIELDS}->{$key}) != exists($pat->{$key}) - || (exists $pat->{$key} - && ($self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS} ne $pat->{$key}))) { - return 0; - } + my $nextkey; + + ($nextkey = $key)++; + # If the next smaller enumeration exists, and is numbered + # continuously, then we don't need to check this one, because + # gaps in issue numbering matter, not changes in volume numbering + next + if (exists $self->{_mfhdh_SUBFIELDS}->{$nextkey} + && !$caption->capfield($nextkey)->{RESTART}); + + # If a subfield exists in $self but not in $pat, or vice versa + # or if the field has different values, then fail + if ( + exists($self->{_mfhdh_SUBFIELDS}->{$key}) != exists($pat->{$key}) + || (exists $pat->{$key} + && ($self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS} ne + $pat->{$key})) + ) { + return 0; + } } return 1; } -# +# # Check that all the fields in a holdings statement are # included in the corresponding caption. -# +# sub validate { my $self = shift; foreach my $key (keys %{$self->{_mfhdh_SUBFIELDS}}) { - if (!$self->{_mfhdh_CAPTION} || !$self->{_mfhdh_CAPTION}->capfield($key)) { - return 0; - } + if ( !$self->{_mfhdh_CAPTION} + || !$self->{_mfhdh_CAPTION}->capfield($key)) { + return 0; + } } return 1; } diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/test/mfhd.t b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/test/mfhd.t index 003df38ebc..8bbf93dd62 100644 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/test/mfhd.t +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/test/mfhd.t @@ -12,13 +12,13 @@ my $testno = 0; sub right_answer { my $holding = shift; - my $answer = {}; + my $answer = {}; foreach my $subfield (split(/\|/, $holding->subfield('x'))) { - next unless $subfield; + next unless $subfield; - my ($key, $val) = unpack('aa*', $subfield); - $answer->{$key} = $val; + my ($key, $val) = unpack('aa*', $subfield); + $answer->{$key} = $val; } return $answer; @@ -31,8 +31,8 @@ sub load_MARC_rec { # skim to beginning of record (a non-blank, non comment line) while ($line = ) { - chomp $line; - last if (!($line =~ /^\s*$/) && !($line =~ /^#/)); + chomp $line; + last if (!($line =~ /^\s*$/) && !($line =~ /^#/)); } return undef if !$line; @@ -42,35 +42,39 @@ sub load_MARC_rec { carp('No record created!') unless $marc; $marc->leader('01119nas 2200313 a 4500'); - $marc->append_fields(MARC::Field->new('008', '970701c18439999enkwr p 0 a0eng ')); - $marc->append_fields(MARC::Field->new('035', '', '', - a => sprintf('%04d', $testno))); + $marc->append_fields( + MARC::Field->new('008', '970701c18439999enkwr p 0 a0eng ')); + $marc->append_fields( + MARC::Field->new('035', '', '', a => sprintf('%04d', $testno))); while ($line) { - next if $line =~ /^#/; # allow embedded comments + next if $line =~ /^#/; # allow embedded comments - return $marc if $line =~ /^\s*$/; + return $marc if $line =~ /^\s*$/; - my ($fieldno, $indicators, $rest) = split(/ /, $line, 3); - my @inds = unpack('cc', $indicators); - my $field; - my @subfields; + my ($fieldno, $indicators, $rest) = split(/ /, $line, 3); + my @inds = unpack('cc', $indicators); + my $field; + my @subfields; - @subfields = (); - foreach my $subfield (split(/\$/, $rest)) { - next unless $subfield; + @subfields = (); + foreach my $subfield (split(/\$/, $rest)) { + next unless $subfield; - my ($key, $val) = unpack('aa*', $subfield); - push @subfields, $key, $val; - } + my ($key, $val) = unpack('aa*', $subfield); + push @subfields, $key, $val; + } - $field = MARC::Field->new($fieldno, $inds[0], $inds[1], - a => 'scratch', @subfields); + $field = MARC::Field->new( + $fieldno, $inds[0], $inds[1], + a => 'scratch', + @subfields + ); - $marc->append_fields($field); + $marc->append_fields($field); - $line = ; - chomp $line if $line; + $line = ; + chomp $line if $line; } return $marc; } @@ -81,22 +85,22 @@ my @captions; while ($rec = load_MARC_rec) { $rec = MFHD->new($rec); - foreach my $cap (sort {$a->tag <=> $b->tag} $rec->field('85.')) { - my $htag; - my @holdings; - - ($htag = $cap->tag) =~ s/^85/86/; - @holdings = $rec->holdings($htag, $cap->subfield('8')); - - next unless scalar @holdings; - foreach my $field (@holdings) { - TODO: { - local $TODO = "unimplemented" - if ($field->subfield('z') =~ /^TODO/); - is_deeply($field->next, right_answer($field), - $field->subfield('8') . ': ' . $field->subfield('z')); - } - } + foreach my $cap (sort { $a->tag <=> $b->tag } $rec->field('85.')) { + my $htag; + my @holdings; + + ($htag = $cap->tag) =~ s/^85/86/; + @holdings = $rec->holdings($htag, $cap->subfield('8')); + + next unless scalar @holdings; + foreach my $field (@holdings) { + TODO: { + local $TODO = "unimplemented" + if ($field->subfield('z') =~ /^TODO/); + is_deeply($field->next, right_answer($field), + $field->subfield('8') . ': ' . $field->subfield('z')); + } + } } } diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm index 33725d61ab..374a85ebaf 100644 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm @@ -1,5 +1,6 @@ package OpenILS::Utils::MFHDParser; -use strict; use warnings; +use strict; +use warnings; use OpenSRF::EX qw/:try/; use Time::HiRes qw(time); @@ -11,7 +12,7 @@ use OpenILS::Utils::MFHD; use MARC::File::XML (BinaryEncoding => 'utf8'); use Data::Dumper; -sub new { return bless( {}, shift() ); } +sub new { return bless({}, shift()); } =head1 Subroutines @@ -26,20 +27,20 @@ Returns concatenated subfields $a with $z for textual holdings (866-868) =cut sub format_textual_holdings { - my ($self, $field) = @_; - my $holdings; - my $public_note; - - $holdings = $field->subfield('a'); - if (!$holdings) { - return undef; - } - - $public_note = $field->subfield('z'); - if ($public_note) { - return "$holdings - $public_note"; - } - return $holdings; + my ($self, $field) = @_; + my $holdings; + my $public_note; + + $holdings = $field->subfield('a'); + if (!$holdings) { + return undef; + } + + $public_note = $field->subfield('z'); + if ($public_note) { + return "$holdings - $public_note"; + } + return $holdings; } =over @@ -51,131 +52,143 @@ sub format_textual_holdings { Returns a Perl hash containing fields of interest from the MFHD record =cut + sub mfhd_to_hash { - my ($self, $mfhd_xml) = @_; - - my $marc; - my $mfhd; - - my $location = ''; - my $holdings = []; - my $supplements = []; - my $indexes = []; - my $current_holdings = []; - my $current_supplements = []; - my $current_indexes = []; - my $online = []; # Laurentian extension to MFHD standard - my $missing = []; # Laurentian extension to MFHD standard - my $incomplete = []; # Laurentian extension to MFHD standard - - try { - $marc = MARC::Record->new_from_xml($mfhd_xml); - } otherwise { - $logger->error("Failed to convert MFHD XML to MARC: " . shift()); - $logger->error("Failed MFHD XML: $mfhd_xml"); - }; - - if (!$marc) { - return undef; - } - - try { - $mfhd = MFHD->new($marc); - } otherwise { - $logger->error("Failed to parse MFHD: " . shift()); - $logger->error("Failed MFHD XML: $mfhd_xml"); - }; - - if (!$mfhd) { - return undef; - } - - try { - foreach my $field ($marc->field('852')) { - foreach my $subfield_ref ($field->subfields) { - my ($subfield, $data) = @$subfield_ref; - $location .= $data . " -- "; - } - } - } otherwise { - $logger->error("MFHD location parsing error: " . shift()); - }; - - $location =~ s/ -- $//; - - try { - foreach my $field ($marc->field('866')) { - my $textual_holdings = $self->format_textual_holdings($field); - if ($textual_holdings) { - push @$holdings, $textual_holdings; - } - } - foreach my $field ($marc->field('867')) { - my $textual_holdings = $self->format_textual_holdings($field); - if ($textual_holdings) { - push @$supplements, $textual_holdings; - } - } - foreach my $field ($marc->field('868')) { - my $textual_holdings = $self->format_textual_holdings($field); - if ($textual_holdings) { - push @$indexes, $textual_holdings; - } - } - - foreach my $cap_id ($mfhd->captions('853')) { - my @curr_holdings = $mfhd->holdings('863', $cap_id); - next unless scalar @curr_holdings; - foreach (@curr_holdings) { - push @$current_holdings, $_->format(); - } - } - - foreach my $cap_id ($mfhd->captions('854')) { - my @curr_supplements = $mfhd->holdings('864', $cap_id); - next unless scalar @curr_supplements; - foreach (@curr_supplements) { - push @$current_supplements, $_->format(); - } - } - - foreach my $cap_id ($mfhd->captions('855')) { - my @curr_indexes = $mfhd->holdings('865', $cap_id); - next unless scalar @curr_indexes; - foreach (@curr_indexes) { - push @$current_indexes, $_->format(); - } - } - - # Laurentian extensions - foreach my $field ($marc->field('530')) { - my $online_stmt = $self->format_textual_holdings($field); - if ($online_stmt) { - push @$online, $online_stmt; - } - } - - foreach my $field ($marc->field('590')) { - my $missing_stmt = $self->format_textual_holdings($field); - if ($missing_stmt) { - push @$missing, $missing_stmt; - } - } - - foreach my $field ($marc->field('591')) { - my $incomplete_stmt = $self->format_textual_holdings($field); - if ($incomplete_stmt) { - push @$incomplete, $incomplete_stmt; - } - } - } otherwise { - $logger->error("MFHD statement parsing error: " . shift()); - }; - - return { location => $location, holdings => $holdings, current_holdings => $current_holdings, - supplements => $supplements, current_supplements => $current_supplements, - indexes => $indexes, current_indexes => $current_indexes, - missing => $missing, incomplete => $incomplete, }; + my ($self, $mfhd_xml) = @_; + + my $marc; + my $mfhd; + + my $location = ''; + my $holdings = []; + my $supplements = []; + my $indexes = []; + my $current_holdings = []; + my $current_supplements = []; + my $current_indexes = []; + my $online = []; # Laurentian extension to MFHD standard + my $missing = []; # Laurentian extension to MFHD standard + my $incomplete = []; # Laurentian extension to MFHD standard + + try { + $marc = MARC::Record->new_from_xml($mfhd_xml); + } + otherwise { + $logger->error("Failed to convert MFHD XML to MARC: " . shift()); + $logger->error("Failed MFHD XML: $mfhd_xml"); + }; + + if (!$marc) { + return undef; + } + + try { + $mfhd = MFHD->new($marc); + } + otherwise { + $logger->error("Failed to parse MFHD: " . shift()); + $logger->error("Failed MFHD XML: $mfhd_xml"); + }; + + if (!$mfhd) { + return undef; + } + + try { + foreach my $field ($marc->field('852')) { + foreach my $subfield_ref ($field->subfields) { + my ($subfield, $data) = @$subfield_ref; + $location .= $data . " -- "; + } + } + } + otherwise { + $logger->error("MFHD location parsing error: " . shift()); + }; + + $location =~ s/ -- $//; + + try { + foreach my $field ($marc->field('866')) { + my $textual_holdings = $self->format_textual_holdings($field); + if ($textual_holdings) { + push @$holdings, $textual_holdings; + } + } + foreach my $field ($marc->field('867')) { + my $textual_holdings = $self->format_textual_holdings($field); + if ($textual_holdings) { + push @$supplements, $textual_holdings; + } + } + foreach my $field ($marc->field('868')) { + my $textual_holdings = $self->format_textual_holdings($field); + if ($textual_holdings) { + push @$indexes, $textual_holdings; + } + } + + foreach my $cap_id ($mfhd->captions('853')) { + my @curr_holdings = $mfhd->holdings('863', $cap_id); + next unless scalar @curr_holdings; + foreach (@curr_holdings) { + push @$current_holdings, $_->format(); + } + } + + foreach my $cap_id ($mfhd->captions('854')) { + my @curr_supplements = $mfhd->holdings('864', $cap_id); + next unless scalar @curr_supplements; + foreach (@curr_supplements) { + push @$current_supplements, $_->format(); + } + } + + foreach my $cap_id ($mfhd->captions('855')) { + my @curr_indexes = $mfhd->holdings('865', $cap_id); + next unless scalar @curr_indexes; + foreach (@curr_indexes) { + push @$current_indexes, $_->format(); + } + } + + # Laurentian extensions + foreach my $field ($marc->field('530')) { + my $online_stmt = $self->format_textual_holdings($field); + if ($online_stmt) { + push @$online, $online_stmt; + } + } + + foreach my $field ($marc->field('590')) { + my $missing_stmt = $self->format_textual_holdings($field); + if ($missing_stmt) { + push @$missing, $missing_stmt; + } + } + + foreach my $field ($marc->field('591')) { + my $incomplete_stmt = $self->format_textual_holdings($field); + if ($incomplete_stmt) { + push @$incomplete, $incomplete_stmt; + } + } + } + otherwise { + $logger->error("MFHD statement parsing error: " . shift()); + }; + + return { + location => $location, + holdings => $holdings, + current_holdings => $current_holdings, + supplements => $supplements, + current_supplements => $current_supplements, + indexes => $indexes, + current_indexes => $current_indexes, + missing => $missing, + incomplete => $incomplete, + }; } =over @@ -187,21 +200,22 @@ sub mfhd_to_hash { Initialize the serial virtual record (svr) instance =cut + sub init_holdings_virtual_record { - my $record = Fieldmapper::serial::virtual_record->new; - $record->id(); - $record->location(); - $record->owning_lib(); - $record->holdings([]); - $record->current_holdings([]); - $record->supplements([]); - $record->current_supplements([]); - $record->indexes([]); - $record->current_indexes([]); - $record->online([]); - $record->missing([]); - $record->incomplete([]); - return $record; + my $record = Fieldmapper::serial::virtual_record->new; + $record->id(); + $record->location(); + $record->owning_lib(); + $record->holdings([]); + $record->current_holdings([]); + $record->supplements([]); + $record->current_supplements([]); + $record->indexes([]); + $record->current_indexes([]); + $record->online([]); + $record->missing([]); + $record->incomplete([]); + return $record; } =over @@ -213,35 +227,36 @@ sub init_holdings_virtual_record { Given an MFHD record, return a populated svr instance =cut + sub generate_svr { - my ($self, $id, $mfhd, $owning_lib) = @_; - - if (!$mfhd) { - return undef; - } - - my $record = init_holdings_virtual_record(); - my $holdings = $self->mfhd_to_hash($mfhd); - - $record->id($id); - $record->owning_lib($owning_lib); - - if (!$holdings) { - return $record; - } - - $record->location($holdings->{location}); - $record->holdings($holdings->{holdings}); - $record->current_holdings($holdings->{current_holdings}); - $record->supplements($holdings->{supplements}); - $record->current_supplements($holdings->{current_supplements}); - $record->indexes($holdings->{indexes}); - $record->current_indexes($holdings->{current_indexes}); - $record->online($holdings->{online}); - $record->missing($holdings->{missing}); - $record->incomplete($holdings->{incomplete}); - - return $record; + my ($self, $id, $mfhd, $owning_lib) = @_; + + if (!$mfhd) { + return undef; + } + + my $record = init_holdings_virtual_record(); + my $holdings = $self->mfhd_to_hash($mfhd); + + $record->id($id); + $record->owning_lib($owning_lib); + + if (!$holdings) { + return $record; + } + + $record->location($holdings->{location}); + $record->holdings($holdings->{holdings}); + $record->current_holdings($holdings->{current_holdings}); + $record->supplements($holdings->{supplements}); + $record->current_supplements($holdings->{current_supplements}); + $record->indexes($holdings->{indexes}); + $record->current_indexes($holdings->{current_indexes}); + $record->online($holdings->{online}); + $record->missing($holdings->{missing}); + $record->incomplete($holdings->{incomplete}); + + return $record; } 1;