Bug 15471 [QA Followup] - Revert use of raw method which is no longer used do to...
[koha.git] / Koha / Indexer / RecordReader.pm
1 # This file is part of Koha.
2 #
3 # Copyright (C) 2013 Tamil s.a.r.l.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # 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 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 package Koha::Indexer::RecordReader;
19
20 use Moose;
21
22 with 'MooseX::RW::Reader';
23
24
25 use Modern::Perl;
26 use utf8;
27 use Moose::Util::TypeConstraints;
28 use MARC::Record;
29 use MARC::File::XML;
30 use C4::Context;
31 use C4::Biblio;
32 use C4::Items;
33
34
35 subtype 'Koha::RecordType'
36     => as 'Str',
37     => where { /biblio|authority/i },
38     => message { "$_ is not a valid Koha::RecordType (biblio or authority" };
39
40 subtype 'Koha::RecordSelect'
41     => as 'Str',
42     => where { /all|queue|queue_update|queue_delete/ },
43     => message {
44         "$_ is not a valide Koha::RecordSelect " .
45         "(all or queue or queue_update or queue_delete)"
46     };
47
48
49 has source => (
50     is       => 'rw',
51     isa      => 'Koha::RecordType',
52     required => 1,
53     default  => 'biblio',
54 );
55
56 has select => (
57     is       => 'rw',
58     isa      => 'Koha::RecordSelect',
59     required => 1,
60     default  => 'all',
61 );
62
63 has xml => ( is => 'rw', isa => 'Bool', default => '0' );
64
65 has sth => ( is => 'rw' );
66
67 # Last returned record biblionumber;
68 has id => ( is => 'rw' );
69
70 # Biblio records normalizer, if necessary
71 has normalizer => ( is => 'rw' );
72
73 # Read all records? (or queued records)
74 has allrecords => ( is => 'rw', isa => 'Bool', default => 1 );
75
76 # Mark as done an entry is Zebra queue
77 has sth_queue_done => ( is => 'rw' );
78
79 # Items tag
80 has itemtag => ( is => 'rw' );
81
82 # Las returned record frameworkcode
83 # FIXME: a KohaRecord class should contain this information
84 has frameworkcode => ( is => 'rw', isa => 'Str' );
85
86
87 sub BUILD {
88     my $self = shift;
89     my $dbh  = C4::Context->dbh();
90
91     # Tag containing items
92     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",'');
93     $self->itemtag($itemtag);
94
95     if ( $self->source =~ /biblio/i &&
96          C4::Context->preference('IncludeSeeFromInSearches') )
97     {
98         require Koha::RecordProcessor;
99         my $normalizer = Koha::RecordProcessor->new( { filters => 'EmbedSeeFromHeadings' } );
100         $self->normalizer($normalizer);
101         # Necessary for as_xml method
102         MARC::File::XML->default_record_format( C4::Context->preference('marcflavour') );
103     }
104
105     my $operation = $self->select =~ /update/i
106                     ? 'specialUpdate'
107                     : 'recordDelete';
108     $self->allrecords( $self->select =~ /all/i ? 1 : 0 );
109     my $sql =
110         $self->source =~ /biblio/i
111             ? $self->allrecords
112                 ? "SELECT NULL, biblionumber FROM biblio"
113                 : "SELECT id, biblio_auth_number FROM zebraqueue
114                    WHERE server = 'biblioserver'
115                      AND operation = '$operation' AND done = 0"
116             : $self->allrecords
117                 ? "SELECT NULL, authid FROM auth_header"
118                 : "SELECT id, biblio_auth_number FROM zebraqueue
119                    WHERE server = 'authorityserver'
120                      AND operation = '$operation' AND done = 0";
121     my $sth = $dbh->prepare( $sql );
122     $sth->execute();
123     $self->sth( $sth );
124
125     unless ( $self->allrecords ) {
126         $self->sth_queue_done( $dbh->prepare(
127             "UPDATE zebraqueue SET done=1 WHERE id=?" ) );
128     }
129
130     __PACKAGE__->meta->add_method( 'get' =>
131         $self->source =~ /biblio/i
132             ? $self->xml && !$self->normalizer
133               ? \&get_biblio_xml
134               : \&get_biblio_marc
135             : $self->xml
136               ? \&get_auth_xml
137               : \&get_auth_marc
138     );
139 }
140
141
142
143 sub read {
144     my $self = shift;
145     while ( my ($queue_id, $id) = $self->sth->fetchrow ) {
146         # Suppress entry in zebraqueue table
147         $self->sth_queue_done->execute($queue_id) if $queue_id;
148         if ( my $record = $self->get( $id ) ) {
149             $record = $self->normalizer->process($record) if $self->normalizer;
150             $self->count($self->count+1);
151             $self->id( $id );
152             return $record;
153         }
154     }
155     return 0;
156 }
157
158
159
160 sub get_biblio_xml {
161     my ( $self, $id ) = @_;
162     my$dbh = C4::Context->dbh();
163     my $sth = $dbh->prepare(
164         "SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
165     $sth->execute( $id );
166     my ($marcxml) = $sth->fetchrow;
167
168     # If biblio isn't found in biblioitems, it is searched in
169     # deletedbilioitems. Usefull for delete Zebra requests
170     unless ( $marcxml ) {
171         $sth = $dbh->prepare(
172             "SELECT marcxml FROM deletedbiblioitems WHERE biblionumber=? ");
173         $sth->execute( $id );
174         ($marcxml) = $sth->fetchrow;
175     }
176
177     # Items extraction
178     # FIXME: It slows down drastically biblio records export
179     {
180         my @items = @{ $dbh->selectall_arrayref(
181             "SELECT * FROM items WHERE biblionumber=$id",
182             {Slice => {} } ) };
183         if (@items){
184             my $record = MARC::Record->new;
185             $record->encoding('UTF-8');
186             my @itemsrecord;
187             foreach my $item (@items) {
188                 my $record = Item2Marc($item, $id);
189                 push @itemsrecord, $record->field($self->itemtag);
190             }
191             $record->insert_fields_ordered(@itemsrecord);
192             my $itemsxml = $record->as_xml_record();
193             $marcxml =
194                 substr($marcxml, 0, length($marcxml)-10) .
195                 substr($itemsxml, index($itemsxml, "</leader>\n", 0) + 10);
196         }
197     }
198     return $marcxml;
199 }
200
201
202 # Get biblio record, if the record doesn't exist in biblioitems, it is searched
203 # in deletedbiblioitems.
204 sub get_biblio_marc {
205     my ( $self, $id ) = @_;
206
207     my $dbh = C4::Context->dbh();
208     my $sth = $dbh->prepare(
209         "SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
210     $sth->execute( $id );
211     my ($marcxml) = $sth->fetchrow;
212
213     unless ( $marcxml ) {
214         $sth = $dbh->prepare(
215             "SELECT marcxml FROM deletedbiblioitems WHERE biblionumber=? ");
216         $sth->execute( $id );
217         ($marcxml) = $sth->fetchrow;
218     }
219
220     $marcxml =~ s/[^\x09\x0A\x0D\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]//g;
221     my $record = MARC::Record->new();
222     if ($marcxml) {
223         $record = eval {
224             MARC::Record::new_from_xml( $marcxml, "utf8" ) };
225         if ($@) { warn " problem with: $id : $@ \n$marcxml"; }
226
227         # Items extraction if Koha v3.4 and above
228         # FIXME: It slows down drastically biblio records export
229         if ( $self->itemsextraction ) {
230             my @items = @{ $dbh->selectall_arrayref(
231                 "SELECT * FROM items WHERE biblionumber=$id",
232                 {Slice => {} } ) };
233             if (@items){
234                 my @itemsrecord;
235                 foreach my $item (@items) {
236                     my $record = Item2Marc($item, $id);
237                     push @itemsrecord, $record->field($self->itemtag);
238                 }
239                 $record->insert_fields_ordered(@itemsrecord);
240             }
241         }
242         return $record;
243     }
244     return;
245 }
246
247
248 sub get_auth_xml {
249     my ( $self, $id ) = @_;
250
251     my $dbh = C4::Context->dbh();
252     my $sth = $dbh->prepare(
253         "select marcxml from auth_header where authid=? "  );
254     $sth->execute( $id );
255     my ($xml) = $sth->fetchrow;
256
257     # If authority isn't found we build a mimimalist record
258     # Usefull for delete Zebra requests
259     unless ( $xml ) {
260         return
261             "<record
262                xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
263                xsi:schemaLocation=\"http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\"
264                xmlns=\"http://www.loc.gov/MARC21/slim\">
265              <leader>                        </leader>
266              <controlfield tag=\"001\">$id</controlfield>
267              </record>\n";
268     }
269
270     my $new_xml = '';
271     foreach ( split /\n/, $xml ) {
272         next if /^<collection|^<\/collection/;
273         $new_xml .= "$_\n";
274     }
275     return $new_xml;
276 }
277
278
279 no Moose;
280 1;