]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHDParser.pm
Merge branch 'master' of git.evergreen-ils.org:Evergreen-DocBook into doc_consolidati...
[Evergreen.git] / Open-ILS / src / perlmods / lib / 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, $skip_all_computable) = @_;
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 (!$skip_all_computable) {
146             if (!exists($skip_computable{'basic'})) {
147                 foreach my $cap_id ($mfhd->caption_link_ids('853')) {
148                     my @holdings = $mfhd->holdings('863', $cap_id);
149                     next unless scalar @holdings;
150                     foreach (@holdings) {
151                         push @$basic_holdings, $_->format();
152                     }
153                 }
154                 if (!@$basic_holdings) { # no computed holdings found
155                     $basic_holdings = $basic_holdings_add;
156                     $basic_holdings_add = [];
157                 }
158             } else { # textual are non additional, but primary
159                 $basic_holdings = $basic_holdings_add;
160                 $basic_holdings_add = [];
161             }
162
163             if (!exists($skip_computable{'supplement'})) {
164                 foreach my $cap_id ($mfhd->caption_link_ids('854')) {
165                     my @supplements = $mfhd->holdings('864', $cap_id);
166                     next unless scalar @supplements;
167                     foreach (@supplements) {
168                         push @$supplement_holdings, $_->format();
169                     }
170                 }
171                 if (!@$supplement_holdings) { # no computed holdings found
172                     $supplement_holdings = $supplement_holdings_add;
173                     $supplement_holdings_add = [];
174                 }
175             } else { # textual are non additional, but primary
176                 $supplement_holdings = $supplement_holdings_add;
177                 $supplement_holdings_add = [];
178             }
179
180             if (!exists($skip_computable{'index'})) {
181                 foreach my $cap_id ($mfhd->caption_link_ids('855')) {
182                     my @indexes = $mfhd->holdings('865', $cap_id);
183                     next unless scalar @indexes;
184                     foreach (@indexes) {
185                         push @$index_holdings, $_->format();
186                     }
187                 }
188                 if (!@$index_holdings) { # no computed holdings found
189                     $index_holdings = $index_holdings_add;
190                     $index_holdings_add = [];
191                 }
192             } else { # textual are non additional, but primary
193                 $index_holdings = $index_holdings_add;
194                 $index_holdings_add = [];
195             }
196         }
197
198         # Laurentian extensions
199         foreach my $field ($marc->field('530')) {
200             my $online_stmt = $self->format_textual_holdings($field);
201             if ($online_stmt) {
202                 push @$online, $online_stmt;
203             }
204         }
205
206         foreach my $field ($marc->field('590')) {
207             my $missing_stmt = $self->format_textual_holdings($field);
208             if ($missing_stmt) {
209                 push @$missing, $missing_stmt;
210             }
211         }
212
213         foreach my $field ($marc->field('591')) {
214             my $incomplete_stmt = $self->format_textual_holdings($field);
215             if ($incomplete_stmt) {
216                 push @$incomplete, $incomplete_stmt;
217             }
218         }
219     }
220     otherwise {
221         $logger->error("MFHD statement parsing error: " . shift());
222     };
223
224     return {
225         location                => $location,
226         basic_holdings          => $basic_holdings,
227         basic_holdings_add      => $basic_holdings_add,
228         supplement_holdings     => $supplement_holdings,
229         supplement_holdings_add => $supplement_holdings_add,
230         index_holdings          => $index_holdings,
231         index_holdings_add      => $index_holdings_add,
232         missing                 => $missing,
233         incomplete              => $incomplete,
234         online                  => $online
235     };
236 }
237
238 =over
239
240 =item * init_holdings_virtual_record()
241
242 =back
243
244 Initialize the serial virtual record (svr) instance
245
246 =cut
247
248 sub init_holdings_virtual_record {
249     my $record = Fieldmapper::serial::virtual_record->new;
250     $record->sre_id();
251     $record->location();
252     $record->owning_lib();
253     $record->basic_holdings([]);
254     $record->basic_holdings_add([]);
255     $record->supplement_holdings([]);
256     $record->supplement_holdings_add([]);
257     $record->index_holdings([]);
258     $record->index_holdings_add([]);
259     $record->online([]);
260     $record->missing([]);
261     $record->incomplete([]);
262     return $record;
263 }
264
265 =over
266
267 =item * init_holdings_virtual_record($mfhd)
268
269 =back
270
271 Given an MFHD record, return a populated svr instance
272
273 =cut
274
275 sub generate_svr {
276     my ($self, $id, $mfhd, $owning_lib, $skip_all_computable) = @_;
277
278     if (!$mfhd) {
279         return undef;
280     }
281
282     my $record   = init_holdings_virtual_record();
283     my $holdings = $self->mfhd_to_hash($mfhd, $skip_all_computable);
284
285     $record->sre_id($id);
286     $record->owning_lib($owning_lib);
287
288     if (!$holdings) {
289         return $record;
290     }
291
292     $record->location($holdings->{location});
293     $record->basic_holdings($holdings->{basic_holdings});
294     $record->basic_holdings_add($holdings->{basic_holdings_add});
295     $record->supplement_holdings($holdings->{supplement_holdings});
296     $record->supplement_holdings_add($holdings->{supplement_holdings_add});
297     $record->index_holdings($holdings->{index_holdings});
298     $record->index_holdings_add($holdings->{index_holdings_add});
299     $record->online($holdings->{online});
300     $record->missing($holdings->{missing});
301     $record->incomplete($holdings->{incomplete});
302
303     return $record;
304 }
305
306 1;
307
308 # vim: ts=4:sw=4:noet