]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm
Whitespace. gah.
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Utils / MFHDParser.pm
1 package OpenILS::Utils::MFHDParser;
2 use strict;
3 use warnings;
4
5 use OpenSRF::EX qw/:try/;
6 use Time::HiRes qw(time);
7 use OpenILS::Utils::Fieldmapper;
8 use OpenSRF::Utils::SettingsClient;
9 use OpenSRF::Utils::Logger qw/$logger/;
10
11 use OpenILS::Utils::MFHD;
12 use MARC::File::XML (BinaryEncoding => 'utf8');
13 use Data::Dumper;
14
15 sub new { return bless({}, shift()); }
16
17 =head1 Subroutines
18
19 =over
20
21 =item * format_textual_holdings($field)
22
23 =back
24
25 Returns concatenated subfields $a with $z for textual holdings (866-868)
26
27 =cut
28
29 sub format_textual_holdings {
30     my ($self, $field) = @_;
31     my $holdings;
32     my $public_note;
33
34     $holdings = $field->subfield('a');
35     if (!$holdings) {
36         return undef;
37     }
38
39     $public_note = $field->subfield('z');
40     if ($public_note) {
41         return "$holdings -- $public_note";
42     }
43     return $holdings;
44 }
45
46 =over
47
48 =item * mfhd_to_hash($mfhd_xml)
49
50 =back
51
52 Returns a Perl hash containing fields of interest from the MFHD record
53
54 =cut
55
56 sub mfhd_to_hash {
57     my ($self, $mfhd_xml) = @_;
58
59     my $marc;
60     my $mfhd;
61
62     my $location                = '';
63     my $basic_holdings          = [];
64     my $supplement_holdings     = [];
65     my $index_holdings          = [];
66     my $basic_holdings_add      = [];
67     my $supplement_holdings_add = [];
68     my $index_holdings_add      = [];
69     my $online                  = [];    # Laurentian extension to MFHD standard
70     my $missing                 = [];    # Laurentian extension to MFHD standard
71     my $incomplete              = [];    # Laurentian extension to MFHD standard
72
73     try {
74         $marc = MARC::Record->new_from_xml($mfhd_xml);
75     }
76     otherwise {
77         $logger->error("Failed to convert MFHD XML to MARC: " . shift());
78         $logger->error("Failed MFHD XML: $mfhd_xml");
79     };
80
81     if (!$marc) {
82         return undef;
83     }
84
85     try {
86         $mfhd = MFHD->new($marc);
87     }
88     otherwise {
89         $logger->error("Failed to parse MFHD: " . shift());
90         $logger->error("Failed MFHD XML: $mfhd_xml");
91     };
92
93     if (!$mfhd) {
94         return undef;
95     }
96
97     try {
98         foreach my $field ($marc->field('852')) {
99             foreach my $subfield_ref ($field->subfields) {
100                 my ($subfield, $data) = @$subfield_ref;
101                 $location .= $data . " -- ";
102             }
103         }
104     }
105     otherwise {
106         $logger->error("MFHD location parsing error: " . shift());
107     };
108
109     $location =~ s/ -- $//;
110
111     # TODO: for now, we will assume that textual holdings are in addition to the 
112     # computable holdings (that is, they have link IDs greater than the 85X fields)
113     # or that they fully replace the computable holdings (checking for link ID '0').
114     # Eventually this may be handled better by format_holdings() in MFHD.pm
115     my %skip_computable;
116     try {
117         foreach my $field ($marc->field('866')) {
118             my $textual_holdings = $self->format_textual_holdings($field);
119             if ($textual_holdings) {
120                 push @$basic_holdings_add, $textual_holdings;
121                 if ($field->subfield('8') eq '0') {
122                    $skip_computable{'basic'} = 1; # link ID 0 trumps computable fields
123                 }
124             }
125         }
126         foreach my $field ($marc->field('867')) {
127             my $textual_holdings = $self->format_textual_holdings($field);
128             if ($textual_holdings) {
129                 push @$supplement_holdings_add, $textual_holdings;
130                 if ($field->subfield('8') eq '0') {
131                    $skip_computable{'supplement'} = 1; # link ID 0 trumps computable fields
132                 }
133             }
134         }
135         foreach my $field ($marc->field('868')) {
136             my $textual_holdings = $self->format_textual_holdings($field);
137             if ($textual_holdings) {
138                 push @$index_holdings_add, $textual_holdings;
139                 if ($field->subfield('8') eq '0') {
140                    $skip_computable{'index'} = 1; # link ID 0 trumps computable fields
141                 }
142             }
143         }
144
145         if (!exists($skip_computable{'basic'})) {
146             foreach my $cap_id ($mfhd->caption_link_ids('853')) {
147                 my @holdings = $mfhd->holdings('863', $cap_id);
148                 next unless scalar @holdings;
149                 foreach (@holdings) {
150                     push @$basic_holdings, $_->format();
151                 }
152             }
153             if (!@$basic_holdings) { # no computed holdings found
154                 $basic_holdings = $basic_holdings_add;
155                 $basic_holdings_add = [];
156             }
157         } else { # textual are non additional, but primary
158             $basic_holdings = $basic_holdings_add;
159             $basic_holdings_add = [];
160         }
161
162         if (!exists($skip_computable{'supplement'})) {
163             foreach my $cap_id ($mfhd->caption_link_ids('854')) {
164                 my @supplements = $mfhd->holdings('864', $cap_id);
165                 next unless scalar @supplements;
166                 foreach (@supplements) {
167                     push @$supplement_holdings, $_->format();
168                 }
169             }
170             if (!@$supplement_holdings) { # no computed holdings found
171                 $supplement_holdings = $supplement_holdings_add;
172                 $supplement_holdings_add = [];
173             }
174         } else { # textual are non additional, but primary
175             $supplement_holdings = $supplement_holdings_add;
176             $supplement_holdings_add = [];
177         }
178
179         if (!exists($skip_computable{'index'})) {
180             foreach my $cap_id ($mfhd->caption_link_ids('855')) {
181                 my @indexes = $mfhd->holdings('865', $cap_id);
182                 next unless scalar @indexes;
183                 foreach (@indexes) {
184                     push @$index_holdings, $_->format();
185                 }
186             }
187             if (!@$index_holdings) { # no computed holdings found
188                 $index_holdings = $index_holdings_add;
189                 $index_holdings_add = [];
190             }
191         } else { # textual are non additional, but primary
192             $index_holdings = $index_holdings_add;
193             $index_holdings_add = [];
194         }
195
196         # Laurentian extensions
197         foreach my $field ($marc->field('530')) {
198             my $online_stmt = $self->format_textual_holdings($field);
199             if ($online_stmt) {
200                 push @$online, $online_stmt;
201             }
202         }
203
204         foreach my $field ($marc->field('590')) {
205             my $missing_stmt = $self->format_textual_holdings($field);
206             if ($missing_stmt) {
207                 push @$missing, $missing_stmt;
208             }
209         }
210
211         foreach my $field ($marc->field('591')) {
212             my $incomplete_stmt = $self->format_textual_holdings($field);
213             if ($incomplete_stmt) {
214                 push @$incomplete, $incomplete_stmt;
215             }
216         }
217     }
218     otherwise {
219         $logger->error("MFHD statement parsing error: " . shift());
220     };
221
222     return {
223         location                => $location,
224         basic_holdings          => $basic_holdings,
225         basic_holdings_add      => $basic_holdings_add,
226         supplement_holdings     => $supplement_holdings,
227         supplement_holdings_add => $supplement_holdings_add,
228         index_holdings          => $index_holdings,
229         index_holdings_add      => $index_holdings_add,
230         missing                 => $missing,
231         incomplete              => $incomplete,
232         online                  => $online
233     };
234 }
235
236 =over
237
238 =item * init_holdings_virtual_record()
239
240 =back
241
242 Initialize the serial virtual record (svr) instance
243
244 =cut
245
246 sub init_holdings_virtual_record {
247     my $record = Fieldmapper::serial::virtual_record->new;
248     $record->sre_id();
249     $record->location();
250     $record->owning_lib();
251     $record->basic_holdings([]);
252     $record->basic_holdings_add([]);
253     $record->supplement_holdings([]);
254     $record->supplement_holdings_add([]);
255     $record->index_holdings([]);
256     $record->index_holdings_add([]);
257     $record->online([]);
258     $record->missing([]);
259     $record->incomplete([]);
260     return $record;
261 }
262
263 =over
264
265 =item * init_holdings_virtual_record($mfhd)
266
267 =back
268
269 Given an MFHD record, return a populated svr instance
270
271 =cut
272
273 sub generate_svr {
274     my ($self, $id, $mfhd, $owning_lib) = @_;
275
276     if (!$mfhd) {
277         return undef;
278     }
279
280     my $record   = init_holdings_virtual_record();
281     my $holdings = $self->mfhd_to_hash($mfhd);
282
283     $record->sre_id($id);
284     $record->owning_lib($owning_lib);
285
286     if (!$holdings) {
287         return $record;
288     }
289
290     $record->location($holdings->{location});
291     $record->basic_holdings($holdings->{basic_holdings});
292     $record->basic_holdings_add($holdings->{basic_holdings_add});
293     $record->supplement_holdings($holdings->{supplement_holdings});
294     $record->supplement_holdings_add($holdings->{supplement_holdings_add});
295     $record->index_holdings($holdings->{index_holdings});
296     $record->index_holdings_add($holdings->{index_holdings_add});
297     $record->online($holdings->{online});
298     $record->missing($holdings->{missing});
299     $record->incomplete($holdings->{incomplete});
300
301     return $record;
302 }
303
304 1;
305
306 # vim: ts=4:sw=4:noet