Line 0
Link Here
|
|
|
1 |
package Koha::Import::Oaipmh; |
2 |
|
3 |
# This file is part of Koha. |
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 |
|
17 |
use Modern::Perl; |
18 |
use XML::LibXML::Reader; |
19 |
use MARC::Record; |
20 |
use DateTime::Format::Strptime; |
21 |
|
22 |
use Koha::Import::Oaipmh::Biblios; |
23 |
use C4::Context; |
24 |
use C4::Biblio; |
25 |
|
26 |
my $xpc = XML::LibXML::XPathContext->new(); |
27 |
$xpc->registerNs('oai','http://www.openarchives.org/OAI/2.0/'); |
28 |
my $xpath_identifier = XML::LibXML::XPathExpression->new("oai:header/oai:identifier"); |
29 |
my $xpath_datestamp = XML::LibXML::XPathExpression->new("oai:header/oai:datestamp"); |
30 |
my $xpath_status = XML::LibXML::XPathExpression->new(q{oai:header/@status}); |
31 |
my $xpath_metadata = XML::LibXML::XPathExpression->new(q{oai:metadata/*}); |
32 |
|
33 |
my $strp = DateTime::Format::Strptime->new( |
34 |
pattern => '%F %T', |
35 |
time_zone => 'floating', |
36 |
); |
37 |
|
38 |
=head1 API Model |
39 |
|
40 |
=head2 Methods |
41 |
|
42 |
=cut |
43 |
|
44 |
sub new { |
45 |
my ($class, $args) = @_; |
46 |
$args = {} unless defined $args; |
47 |
return bless ($args, $class); |
48 |
} |
49 |
|
50 |
#NOTE: To improve performance, you could turn off autocommit at the start of this function, then commit at the end of the function |
51 |
sub process_metadata { |
52 |
my ($self, $args) = @_; |
53 |
my $added = 0; |
54 |
my $updated = 0; |
55 |
my $deleted = 0; |
56 |
my $skipped = 0; |
57 |
|
58 |
my $repository = $args->{repository}; |
59 |
my $records = $args->{records}; |
60 |
my $frameworkcode = $args->{frameworkcode} // ''; |
61 |
if ($repository && $records) { |
62 |
my $reader = XML::LibXML::Reader->new( string => $records ); |
63 |
my $each_pattern = XML::LibXML::Pattern->new('//oai:record', { 'oai' => "http://www.openarchives.org/OAI/2.0/" }); |
64 |
while (my $rv = $reader->nextPatternMatch($each_pattern)){ |
65 |
#NOTE: We do this so we only get the opening tag of the element. |
66 |
next unless $reader->nodeType == XML_READER_TYPE_ELEMENT; |
67 |
if ($reader->localName eq "record"){ |
68 |
my $record_node = $reader->copyCurrentNode(1); |
69 |
if ($record_node){ |
70 |
my $document = XML::LibXML::Document->new('1.0', 'UTF-8'); |
71 |
$document->setDocumentElement($record_node); |
72 |
my $root = $document->documentElement; |
73 |
|
74 |
my $identifier = _get_node_text({ |
75 |
root => $root, |
76 |
xpath_expression => $xpath_identifier, |
77 |
}); |
78 |
my $datestamp = _get_node_text({ |
79 |
root => $root, |
80 |
xpath_expression => $xpath_datestamp, |
81 |
}); |
82 |
#Remove UTC markers from timestamp |
83 |
$datestamp =~ s/T/ /; |
84 |
$datestamp =~ s/Z//; |
85 |
|
86 |
my $metadata; |
87 |
my $metadata_node = _get_node({ |
88 |
root => $root, |
89 |
xpath_expression => $xpath_metadata, |
90 |
}); |
91 |
if ($metadata_node){ |
92 |
$metadata = $metadata_node->toString(2); |
93 |
} |
94 |
|
95 |
my $deleted = _get_node_text({ |
96 |
root => $root, |
97 |
xpath_expression => $xpath_status, |
98 |
}); |
99 |
|
100 |
if ($self->{type} && $self->{type} eq 'biblio'){ |
101 |
if ($identifier && $datestamp){ |
102 |
my $record = Koha::Import::Oaipmh::Biblios->find({ |
103 |
repository => $repository, |
104 |
identifier => $identifier, |
105 |
}); |
106 |
if ($record){ |
107 |
if ($metadata && ! $deleted){ |
108 |
eval { |
109 |
my $incoming_dt = $strp->parse_datetime($datestamp); |
110 |
my $existing_dt = $strp->parse_datetime( $record->datestamp ); |
111 |
warn $incoming_dt; |
112 |
warn $existing_dt; |
113 |
if ( $incoming_dt > $existing_dt ){ |
114 |
MARC::Record::default_record_format("UNIMARC") |
115 |
if ( C4::Context->preference("marcflavour") eq "UNIMARC" ); |
116 |
my $marc_record = MARC::Record::new_from_xml( $metadata, 'UTF-8' ); |
117 |
C4::Biblio::ModBiblio($marc_record, $record->biblionumber, $frameworkcode); |
118 |
$record->update({ |
119 |
datestamp => $datestamp, |
120 |
metadata => $metadata, |
121 |
}); |
122 |
$updated++; |
123 |
} else { |
124 |
$skipped++; |
125 |
} |
126 |
}; |
127 |
} elsif ($deleted){ |
128 |
eval { |
129 |
C4::Biblio::DelBiblio($record->biblionumber); |
130 |
#$record->update({ |
131 |
# datestamp => $datestamp, |
132 |
# deleted => 1, |
133 |
# metadata => '' |
134 |
#}); |
135 |
$deleted++; |
136 |
}; |
137 |
} |
138 |
} else { |
139 |
#NOTE: Having metadata and being deleted should be mutually exclusive, |
140 |
#but it doesn't hurt to be thorough |
141 |
if ($metadata && ! $deleted){ |
142 |
eval { |
143 |
MARC::Record::default_record_format("UNIMARC") |
144 |
if ( C4::Context->preference("marcflavour") eq "UNIMARC" ); |
145 |
my $marc_record = MARC::Record::new_from_xml( $metadata, 'UTF-8' ); |
146 |
my ($biblionumber) = C4::Biblio::AddBiblio($marc_record,$frameworkcode); |
147 |
Koha::Import::Oaipmh::Biblio->new({ |
148 |
repository => $repository, |
149 |
identifier => $identifier, |
150 |
datestamp => $datestamp, |
151 |
metadata => $metadata, |
152 |
frameworkcode => $frameworkcode, |
153 |
biblionumber => $biblionumber, |
154 |
})->store; |
155 |
$added++; |
156 |
}; |
157 |
} |
158 |
} |
159 |
} |
160 |
} |
161 |
} |
162 |
} |
163 |
} |
164 |
} |
165 |
my $results = { |
166 |
added => $added, |
167 |
updated => $updated, |
168 |
deleted => $deleted, |
169 |
skipped => $skipped, |
170 |
}; |
171 |
return $results; |
172 |
} |
173 |
|
174 |
sub _get_node { |
175 |
my ($args) = @_; |
176 |
my $node; |
177 |
my $root = $args->{root}; |
178 |
my $xpath_expression = $args->{xpath_expression}; |
179 |
if ($root && $xpath_expression){ |
180 |
my $node_list = $xpc->findnodes($xpath_expression,$root); |
181 |
if ($node_list){ |
182 |
my $first_node = $node_list->shift; |
183 |
if ($first_node){ |
184 |
$node = $first_node; |
185 |
} |
186 |
} |
187 |
} |
188 |
return $node; |
189 |
} |
190 |
|
191 |
sub _get_node_text { |
192 |
my ($args) = @_; |
193 |
my $text; |
194 |
my $root = $args->{root}; |
195 |
my $xpath_expression = $args->{xpath_expression}; |
196 |
if ($root && $xpath_expression){ |
197 |
my $node = _get_node({ |
198 |
root => $root, |
199 |
xpath_expression => $xpath_expression, |
200 |
}); |
201 |
if ($node){ |
202 |
$text = $node->textContent; |
203 |
} |
204 |
} |
205 |
return $text; |
206 |
} |
207 |
|
208 |
1; |