From a22922f80de4d617f11cc2d48cc6b3f5a941a23b Mon Sep 17 00:00:00 2001 From: djfiander Date: Wed, 25 Nov 2009 22:44:34 +0000 Subject: [PATCH] CHANGES . Added more comments . Removed OpenSRF dependency entirely . Added compressed_to_last() method . Added setter functionality to is_compressed() method (needed for compressed_to_last()) . Replaced hardcoded 'Note:' with double-dash (should we consider some basic template support?) . Fixed a few small bugs and typos ================================================ Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. Signed-off-by: Dan Wells git-svn-id: svn://svn.open-ils.org/ILS/trunk@15033 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm | 30 ++++++-- .../src/perlmods/OpenILS/Utils/MFHD/Caption.pm | 20 +++--- .../src/perlmods/OpenILS/Utils/MFHD/Holding.pm | 83 ++++++++++++++++++++-- Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm | 2 +- 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm index cf74a6171d..b7d7686fe6 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm @@ -8,7 +8,6 @@ use Data::Dumper; use base 'MARC::Record'; -# use OpenSRF::Utils::JSON; use OpenILS::Utils::MFHD::Caption; use OpenILS::Utils::MFHD::Holding; @@ -130,6 +129,23 @@ sub holdings { values %{$self->{_mfhd_HOLDINGS}->{$field}->{$capid}}; } +# +# generate_predictions() is an initial attempt at a function which can be used +# to populate an issuance table with a list of predicted issues. It accepts +# a hash ref of options initially defined as: +# field : the caption field to predict on (853, 854, or 855) +# num_to_predict : the number of issues you wish to predict +# last_rec_date : the date of the last received issue, to be used as an offset +# for predicting future issues +# +# The basic method is to first convert to a single holding if compressed, then +# increment the holding and save the resulting values to @predictions. +# +# returns @preditions, an array of array refs containing (link id, formatted +# label, formatted chronology date, formatted estimated arrival date, and an +# array ref of holding subfields as (key, value, key, value ...)) (not a hash +# to protect order and possible duplicate keys). +# sub generate_predictions { my ($self, $options) = @_; my $field = $options->{field}; @@ -154,25 +170,29 @@ sub generate_predictions { my @holdings = $self->holdings($htag, $link_id); my $last_holding = $holdings[-1]; + if ($last_holding->is_compressed) { + $last_holding->compressed_to_last; # convert to last in range + } + my $pub_date = $strp->parse_datetime($last_holding->chron_to_date); my $date_diff = $receival_date - $pub_date; $last_holding->notes('public', []); + # add a note marker for system use $last_holding->notes('private', ['AUTOGEN']); for (my $i = 0; $i < $num_to_predict; $i++) { $last_holding->increment; $pub_date = $strp->parse_datetime($last_holding->chron_to_date); - $pub_date = $pub_date + $date_diff; + my $arrival_date = $pub_date + $date_diff; push( @predictions, [ $link_id, $last_holding->format, $pub_date->strftime('%F'), -# OpenSRF::Utils::JSON->perl2JSON( -# [$last_holding->subfields_list] -# ) + $arrival_date->strftime('%F'), + [$last_holding->subfields_list] ] ); } diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm index 5255c0da78..35f1db7982 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm @@ -682,19 +682,21 @@ sub next { my $holding = shift; my $next = {}; + # If the holding is compressed and not open ended, base next() on the + # closing date. If the holding is open-ended, next() is undefined + my $index; + if ($holding->is_compressed) { + return undef if $holding->is_open_ended; + # TODO: error on next for open-ended holdings? + $index = 1; + } else { + $index = 0; + } + # Initialize $next with current enumeration & chronology, then # we can just operate on $next, based on the contents of the caption foreach my $key ('a'..'m') { my $holding_values = $holding->field_values($key); - my $index; - if ($holding->is_compressed) { - return undef - if $holding->is_open_ended; - # TODO: error on next for open-ended holdings? - $index = 1; - } else { - $index = 0; - } $next->{$key} = ${$holding_values}[$index] if defined $holding_values; } diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm index 9405d20d6d..4ce15f256d 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm @@ -31,7 +31,8 @@ sub new { $self->{_mfhdh_NOTES}{public} = []; $self->{_mfhdh_NOTES}{private} = []; $self->{_mfhdh_COPYRIGHT} = []; - $self->{_mfhdh_COMPRESSED} = $self->indicator(2) eq '0' ? 1 : 0; + $self->{_mfhdh_COMPRESSED} = ($self->indicator(2) eq '0' || $self->indicator(2) eq '2') ? 1 : 0; + # TODO: full support for second indicators 2, 3, and 4 $self->{_mfhdh_OPEN_ENDED} = 0; foreach my $subfield ($self->subfields) { @@ -85,6 +86,9 @@ sub new { # than simply the MARC subfields, although in the current implementation they # are indexed on the subfield key # +# TODO: this accessor should probably be replaced with methods which hide the +# underlying structure of {_mfhdh_FIELDS} (see field_values for a start) +# sub fields { my $self = shift; @@ -95,6 +99,10 @@ sub fields { # Given a field key, returns an array ref of one (for single statements) # or two (for compressed statements) values # +# TODO: add setter functionality to replace direct {HOLDINGS} access in other +# methods. It also makes sense to override some of the MARC::Field setter +# methods (such as update()) to accomplish this level of encapsulation. +# sub field_values { my ($self, $key) = @_; @@ -117,8 +125,23 @@ sub seqno { return $self->{_mfhdh_SEQNO}; } +# +# Optionally accepts a true/false value to set the 'compressed' attribute +# Returns 'compressed' attribute +# sub is_compressed { my $self = shift; + my $is_compressed = shift; + + if (defined($is_compressed)) { + if ($is_compressed) { + $self->{_mfhdh_COMPRESSED} = 1; + $self->update(ind2 => '0'); + } else { + $self->{_mfhdh_COMPRESSED} = 0; + $self->update(ind2 => '1'); + } + } return $self->{_mfhdh_COMPRESSED}; } @@ -135,6 +158,18 @@ sub caption { return $self->{_mfhdh_CAPTION}; } +# +# notes: If called with no arguments, returns the public notes array ref. +# If called with a single argument, it returns either 'public' or +# 'private' notes based on the passed string. +# +# If called with more than one argument, it sets the proper note field, with +# type being the first argument and the note value(s) as the remaining +# argument(s). +# +# It is also optional to pass in an array ref of note values as the third +# argument rather than a list. +# sub notes { my $self = shift; my $type = shift; @@ -143,7 +178,7 @@ sub notes { if (!$type) { $type = 'public'; } elsif ($type ne 'public' && $type ne 'private') { - carp("Notes being applied without specifiying type"); + carp("Notes being applied without specifying type"); unshift(@notes, $type); $type = 'public'; } @@ -242,7 +277,7 @@ sub format_chron { # account for possible combined issue chronology my @chron_parts = split('/', $holdings->{$key}); for (my $i = 0; $i < @chron_parts; $i++) { - $chron_parts[$i] = $month{$chron_parts[$i]}; + $chron_parts[$i] = $month{$chron_parts[$i]} if exists $month{$chron_parts[$i]}; } $chron = join('/', @chron_parts); } else { @@ -367,7 +402,7 @@ sub format { # Public Note if (@{$self->notes}) { - $formatted .= ' Note: ' . join(', ', @{$self->notes}); + $formatted .= ' -- ' . join(', ', @{$self->notes}); } return $formatted; @@ -436,6 +471,11 @@ sub validate { sub increment { my $self = shift; + if ($self->is_open_ended) { + carp "Holding is open-ended, cannot increment"; + return $self; + } + my $next = $self->next(); if ($self->is_compressed) { # expand range @@ -457,6 +497,34 @@ sub increment { } # +# Turns a compressed holding into the singular form of the last member +# in the range +# +sub compressed_to_last { + my $self = shift; + + if (!$self->is_compressed) { + carp "Holding not compressed, cannot convert to last member"; + return $self; + } elsif ($self->is_open_ended) { + carp "Holding is open-ended, cannot convert to last member"; + return $self; + } + + my %changes; + foreach my $key (keys %{$self->fields}) { + my @values = @{$self->field_values($key)}; + $self->fields->{$key}{HOLDINGS} = [$values[1]]; + $changes{$key} = $values[1]; + } + + $self->update(%changes); # update underlying subfields + $self->is_compressed(0); # remove compressed state + + return $self; +} + +# # Basic, working, unoptimized clone operation # sub clone { @@ -486,6 +554,9 @@ sub chron_to_date { @keys = ('i'..'m'); } + # @chron_start and @chron_end will hold the (year, month, day) values + # represented by the start and optional end of the chronology instance. + # Default to January 1 with a year of 0 as initial values. my @chron_start = (0, 1, 1); my @chron_end = (0, 1, 1); my @chrons = (\@chron_start, \@chron_end); @@ -499,6 +570,8 @@ sub chron_to_date { } elsif ($capstr =~ /day/) { ($chron_start[2], $chron_end[2]) = @{$self->field_values($key)}; } elsif ($capstr =~ /season/) { + # chrons defined as season-only will use the astronomical season + # dates as a basic estimate. my @seasons = @{$self->field_values($key)}; for (my $i = 0; $i < @seasons; $i++) { $seasons[$i] = &_uncombine($seasons[$i], 0); @@ -547,7 +620,7 @@ sub _uncombine { my ($combo, $pos) = @_; if (ref($combo)) { - carp("Function 'uncombine' is not an instance method"); + carp("Function '_uncombine' is not an instance method"); return; } diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm index 32957eefb9..2e49cc0778 100644 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm @@ -38,7 +38,7 @@ sub format_textual_holdings { $public_note = $field->subfield('z'); if ($public_note) { - return "$holdings - $public_note"; + return "$holdings -- $public_note"; } return $holdings; } -- 2.11.0