]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/support-scripts/authority_control_fields.pl
edi_translator sample EDI/JEDI docs
[working/Evergreen.git] / Open-ILS / src / support-scripts / authority_control_fields.pl
1 #!/usr/bin/perl
2 # Copyright (C) 2010 Laurentian University
3 # Author: Dan Scott <dscott@laurentian.ca>
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
15
16 use strict;
17 use warnings;
18 use DBI;
19 use Getopt::Long;
20 use MARC::Record;
21 use MARC::File::XML;
22 use OpenSRF::System;
23 use OpenILS::Utils::Fieldmapper;
24 use OpenSRF::Utils::SettingsClient;
25 use Encode;
26 use Unicode::Normalize;
27 use OpenILS::Application::AppUtils;
28 use Data::Dumper;
29
30 =head1
31
32 For a given set of records (specified by ID at the command line, or special option --all):
33
34 =over
35
36 =item * Iterate through the list of fields that are controlled fields
37
38 =item * Iterate through the list of subfields that are controlled for
39 that given field
40
41 =item * Search for a matching authority record for that combination of
42 field + subfield(s)
43
44 =over
45
46 =item * If we find a match, then add a $0 subfield to that field identifying
47 the controlling authority record
48
49 =item * If we do not find a match, then insert a row into an "uncontrolled"
50 table identifying the record ID, field, and subfield(s) that were not controlled
51
52 =back
53
54 =item * Iterate through the list of floating subdivisions
55
56 =over
57
58 =item * If we find a match, then add a $0 subfield to that field identifying
59 the controlling authority record
60
61 =item * If we do not find a match, then insert a row into an "uncontrolled"
62 table identifying the record ID, field, and subfield(s) that were not controlled
63
64 =back
65
66 =item * If we changed the record, update it in the database
67
68 =back
69
70 =cut
71
72 my $all_records;
73 my $bootstrap = '/openils/conf/opensrf_core.xml';
74 my @records;
75 my $result = GetOptions(
76     'configuration=s' => \$bootstrap,
77     'record=s' => \@records,
78     'all' => \$all_records
79 );
80
81 OpenSRF::System->bootstrap_client(config_file => $bootstrap);
82 Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
83
84 # must be loaded and initialized after the IDL is parsed
85 use OpenILS::Utils::CStoreEditor;
86 OpenILS::Utils::CStoreEditor::init();
87
88 my $editor = OpenILS::Utils::CStoreEditor->new;
89 my $undeleted;
90 if ($all_records) {
91     # get a list of all non-deleted records from Evergreen
92     # open-ils.cstore open-ils.cstore.direct.biblio.record_entry.id_list.atomic {"deleted":"f"}
93     $undeleted = $editor->request( 
94         'open-ils.cstore.direct.biblio.record_entry.id_list.atomic', 
95         [{deleted => 'f'}, {id => { '>' => 0}}]
96     );
97     @records = @$undeleted;
98 }
99 # print Dumper($undeleted, \@records);
100
101 # Hash of controlled fields & subfields in bibliographic records, and their
102 # corresponding controlling fields & subfields in the authority record
103 #
104 # So, if the bib 650$a can be controlled by an auth 150$a, that maps to:
105 # 650 => { a => { 150 => 'a'}}
106 my %controllees = (
107     100 => { a => { 100 => 'a' },
108              b => { 100 => 'b' },
109              c => { 100 => 'c' },
110              d => { 100 => 'd' },
111              e => { 100 => 'e' },
112              f => { 100 => 'f' },
113              g => { 100 => 'g' },
114              j => { 100 => 'j' },
115              k => { 100 => 'k' },
116              l => { 100 => 'l' },
117              n => { 100 => 'n' },
118              p => { 100 => 'p' },
119              q => { 100 => 'q' },
120              t => { 100 => 't' },
121              u => { 100 => 'u' },
122              4 => { 100 => '4' },
123     },
124     110 => { a => { 110 => 'a' },
125              b => { 110 => 'b' },
126              c => { 110 => 'c' },
127              d => { 110 => 'd' },
128              e => { 110 => 'e' },
129              f => { 110 => 'f' },
130              g => { 110 => 'g' },
131              k => { 110 => 'k' },
132              l => { 110 => 'l' },
133              n => { 110 => 'n' },
134              p => { 110 => 'p' },
135              t => { 110 => 't' },
136              u => { 110 => 'u' },
137              4 => { 110 => '4' },
138     },
139     111 => { a => { 111 => 'a' },
140              c => { 111 => 'c' },
141              d => { 111 => 'd' },
142              e => { 111 => 'e' },
143              f => { 111 => 'f' },
144              g => { 111 => 'g' },
145              j => { 111 => 'j' },
146              k => { 111 => 'k' },
147              l => { 111 => 'l' },
148              n => { 111 => 'n' },
149              p => { 111 => 'p' },
150              q => { 111 => 'q' },
151              t => { 111 => 't' },
152              u => { 111 => 'u' },
153              4 => { 111 => '4' },
154     },
155     130 => { a => { 130 => 'a' },
156              d => { 130 => 'd' },
157              f => { 130 => 'f' },
158              g => { 130 => 'g' },
159              h => { 130 => 'h' },
160              k => { 130 => 'k' },
161              l => { 130 => 'l' },
162              m => { 130 => 'm' },
163              n => { 130 => 'n' },
164              o => { 130 => 'o' },
165              p => { 130 => 'p' },
166              r => { 130 => 'r' },
167              s => { 130 => 's' },
168              t => { 130 => 't' },
169     },
170     600 => { a => { 100 => 'a' },
171              b => { 100 => 'b' },
172              c => { 100 => 'c' },
173              d => { 100 => 'd' },
174              e => { 100 => 'e' },
175              f => { 100 => 'f' },
176              g => { 100 => 'g' },
177              h => { 100 => 'h' },
178              j => { 100 => 'j' },
179              k => { 100 => 'k' },
180              l => { 100 => 'l' },
181              m => { 100 => 'm' },
182              n => { 100 => 'n' },
183              o => { 100 => 'o' },
184              p => { 100 => 'p' },
185              q => { 100 => 'q' },
186              r => { 100 => 'r' },
187              s => { 100 => 's' },
188              t => { 100 => 't' },
189              v => { 100 => 'v' },
190              x => { 100 => 'x' },
191              y => { 100 => 'y' },
192              z => { 100 => 'z' },
193              4 => { 100 => '4' },
194     },
195     610 => { a => { 110 => 'a' },
196              b => { 110 => 'b' },
197              c => { 110 => 'c' },
198              d => { 110 => 'd' },
199              e => { 110 => 'e' },
200              f => { 110 => 'f' },
201              g => { 110 => 'g' },
202              h => { 110 => 'h' },
203              k => { 110 => 'k' },
204              l => { 110 => 'l' },
205              m => { 110 => 'm' },
206              n => { 110 => 'n' },
207              o => { 110 => 'o' },
208              p => { 110 => 'p' },
209              r => { 110 => 'r' },
210              s => { 110 => 's' },
211              t => { 110 => 't' },
212              v => { 110 => 'v' },
213              x => { 110 => 'x' },
214              y => { 110 => 'y' },
215              z => { 110 => 'z' },
216     },
217     611 => { a => { 111 => 'a' },
218              c => { 111 => 'c' },
219              d => { 111 => 'd' },
220              e => { 111 => 'e' },
221              f => { 111 => 'f' },
222              g => { 111 => 'g' },
223              h => { 111 => 'h' },
224              j => { 111 => 'j' },
225              k => { 111 => 'k' },
226              l => { 111 => 'l' },
227              n => { 111 => 'n' },
228              p => { 111 => 'p' },
229              q => { 111 => 'q' },
230              s => { 111 => 's' },
231              t => { 111 => 't' },
232              v => { 111 => 'v' },
233              x => { 111 => 'x' },
234              y => { 111 => 'y' },
235              z => { 111 => 'z' },
236     },
237     630 => { a => { 130 => 'a' },
238              d => { 130 => 'd' },
239              f => { 130 => 'f' },
240              g => { 130 => 'g' },
241              h => { 130 => 'h' },
242              k => { 130 => 'k' },
243              l => { 130 => 'l' },
244              m => { 130 => 'm' },
245              n => { 130 => 'n' },
246              o => { 130 => 'o' },
247              p => { 130 => 'p' },
248              r => { 130 => 'r' },
249              s => { 130 => 's' },
250              t => { 130 => 't' },
251              v => { 130 => 'v' },
252              x => { 130 => 'x' },
253              y => { 130 => 'y' },
254              z => { 130 => 'z' },
255     },
256     648 => { a => { 148 => 'a' },
257              v => { 148 => 'v' },
258              x => { 148 => 'x' },
259              y => { 148 => 'y' },
260              z => { 148 => 'z' },
261     },
262     650 => { a => { 150 => 'a' },
263              b => { 150 => 'b' },
264              v => { 150 => 'v' },
265              x => { 150 => 'x' },
266              y => { 150 => 'y' },
267              z => { 150 => 'z' },
268     },
269     651 => { a => { 151 => 'a' },
270              v => { 151 => 'v' },
271              x => { 151 => 'x' },
272              y => { 151 => 'y' },
273              z => { 151 => 'z' },
274     },
275     655 => { a => { 155 => 'a' },
276              v => { 155 => 'v' },
277              x => { 155 => 'x' },
278              y => { 155 => 'y' },
279              z => { 155 => 'z' },
280     },
281     700 => { a => { 100 => 'a' },
282              b => { 100 => 'b' },
283              c => { 100 => 'c' },
284              d => { 100 => 'd' },
285              e => { 100 => 'e' },
286              f => { 100 => 'f' },
287              g => { 100 => 'g' },
288              j => { 100 => 'j' },
289              k => { 100 => 'k' },
290              l => { 100 => 'l' },
291              n => { 100 => 'n' },
292              p => { 100 => 'p' },
293              q => { 100 => 'q' },
294              t => { 100 => 't' },
295              u => { 100 => 'u' },
296              4 => { 100 => '4' },
297     },
298     710 => { a => { 110 => 'a' },
299              b => { 110 => 'b' },
300              c => { 110 => 'c' },
301              d => { 110 => 'd' },
302              e => { 110 => 'e' },
303              f => { 110 => 'f' },
304              g => { 110 => 'g' },
305              k => { 110 => 'k' },
306              l => { 110 => 'l' },
307              n => { 110 => 'n' },
308              p => { 110 => 'p' },
309              t => { 110 => 't' },
310              u => { 110 => 'u' },
311              4 => { 110 => '4' },
312     },
313     711 => { a => { 111 => 'a' },
314              c => { 111 => 'c' },
315              d => { 111 => 'd' },
316              e => { 111 => 'e' },
317              f => { 111 => 'f' },
318              g => { 111 => 'g' },
319              j => { 111 => 'j' },
320              k => { 111 => 'k' },
321              l => { 111 => 'l' },
322              n => { 111 => 'n' },
323              p => { 111 => 'p' },
324              q => { 111 => 'q' },
325              t => { 111 => 't' },
326              u => { 111 => 'u' },
327              4 => { 111 => '4' },
328     },
329     730 => { a => { 130 => 'a' },
330              d => { 130 => 'd' },
331              f => { 130 => 'f' },
332              g => { 130 => 'g' },
333              h => { 130 => 'h' },
334              k => { 130 => 'k' },
335              l => { 130 => 'l' },
336              m => { 130 => 'm' },
337              n => { 130 => 'n' },
338              o => { 130 => 'o' },
339              p => { 130 => 'p' },
340              r => { 130 => 'r' },
341              s => { 130 => 's' },
342              t => { 130 => 't' },
343     },
344     751 => { a => { 151 => 'a' },
345              v => { 151 => 'v' },
346              x => { 151 => 'x' },
347              y => { 151 => 'y' },
348              z => { 151 => 'z' },
349     },
350 );
351
352 foreach my $rec_id (@records) {
353     # print "$rec_id\n";
354
355     my $e = OpenILS::Utils::CStoreEditor->new(xact=>1);
356     # State variable; was the record changed?
357     my $changed;
358
359     # get the record
360     my $record = $e->retrieve_biblio_record_entry($rec_id);
361     next unless $record;
362     # print Dumper($record);
363
364     my $marc = MARC::Record->new_from_xml($record->marc());
365
366     # get the list of controlled fields
367     my @c_fields = keys %controllees;
368
369     foreach my $c_tag (@c_fields) {
370         my @c_subfields = keys %{$controllees{"$c_tag"}};
371         # print "Field: $field subfields: ";
372         # foreach (@subfields) { print "$_ "; }
373
374         # Get the MARCXML from the record and check for controlled fields/subfields
375         my @bib_fields = ($marc->field($c_tag));
376         foreach my $bib_field (@bib_fields) {
377             # print $_->as_formatted(); 
378             my %match_subfields;
379             my $match_tag;
380             my @searches;
381             foreach my $c_subfield (@c_subfields) {
382                 my $sf = $bib_field->subfield($c_subfield);
383                 if ($sf) {
384                     # Give me the first element of the list of authority controlling tags for this subfield
385                     # XXX Will we need to support more than one controlling tag per subfield? Probably. That
386                     # will suck. Oh well, leave that up to Ole to implement.
387                     $match_subfields{$c_subfield} = (keys %{$controllees{$c_tag}{$c_subfield}})[0];
388                     $match_tag = $match_subfields{$c_subfield};
389                     push @searches, {term => $sf, subfield => $c_subfield};
390                 }
391             }
392             # print Dumper(\%match_subfields);
393             next if !$match_tag;
394
395             my @tags = ($match_tag);
396
397             # print "Controlling tag: $c_tag and match tag $match_tag\n";
398             # print Dumper(\@tags, \@searches);
399
400             # Now we've built up a complete set of matching controlled
401             # subfields for this particular field; let's check to see if
402             # we have a matching authority record
403             my $session = OpenSRF::AppSession->create("open-ils.search");
404             my $validates = $session->request("open-ils.search.authority.validate.tag.id_list", 
405                 "tags", \@tags, "searches", \@searches
406             )->gather();
407             $session->disconnect();
408
409             # print Dumper($validates);
410
411             if (scalar(@$validates) == 0) {
412                 next;
413             }
414
415             # Iterate through the returned authority record IDs to delete any
416             # matching $0 subfields already in the bib record
417             foreach my $auth_zero (@$validates) {
418                 $bib_field->delete_subfield(code => '0', match => qr/\)$auth_zero$/);
419             }
420
421             # Okay, we have a matching authority control; time to
422             # add the magical subfield 0. Use the first returned auth
423             # record as a match.
424             my $auth_id = @$validates[0];
425             my $auth_rec = $e->retrieve_authority_record_entry($auth_id);
426             my $auth_marc = MARC::Record->new_from_xml($auth_rec->marc());
427             my $cni = $auth_marc->field('003')->data();
428             
429             $bib_field->add_subfields('0' => "($cni)$auth_id");
430             $changed = 1;
431         }
432     }
433     if ($changed) {
434         # print $marc->as_formatted();
435         my $xml = $marc->as_xml_record();
436         $xml =~ s/\n//sgo;
437         $xml =~ s/^<\?xml.+\?\s*>//go;
438         $xml =~ s/>\s+</></go;
439         $xml =~ s/\p{Cc}//go;
440         $xml = OpenILS::Application::AppUtils->entityize($xml);
441
442         $record->marc($xml);
443         $e->update_biblio_record_entry($record);
444     }
445     $e->commit();
446 }