3 # Copyright 2011 BibLibre
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 C4::OAI::Sets - OAI Sets management functions
26 C4::OAI::Sets contains functions for managing storage and editing of OAI Sets.
28 OAI Set description can be found L<here|http://www.openarchives.org/OAI/openarchivesprotocol.html#Set>
34 use Koha::Biblio::Metadata;
36 use vars qw(@ISA @EXPORT);
42 GetOAISets GetOAISet GetOAISetBySpec ModOAISet DelOAISet AddOAISet
43 GetOAISetsMappings GetOAISetMappings ModOAISetMappings
44 GetOAISetsBiblio ModOAISetsBiblios AddOAISetsBiblios
45 CalcOAISetsBiblio UpdateOAISetsBiblio DelOAISetsBiblio
53 $oai_sets = GetOAISets;
55 GetOAISets return a array reference of hash references describing the sets.
56 The hash references looks like this:
71 my $dbh = C4::Context->dbh;
73 SELECT * FROM oai_sets
75 my $sth = $dbh->prepare($query);
77 my $results = $sth->fetchall_arrayref({});
81 FROM oai_sets_descriptions
84 $sth = $dbh->prepare($query);
85 foreach my $set (@$results) {
86 $sth->execute($set->{'id'});
87 my $desc = $sth->fetchall_arrayref({});
89 push @{$set->{'descriptions'}}, $_->{'description'};
98 $set = GetOAISet($set_id);
100 GetOAISet returns a hash reference describing the set with the given set_id.
102 See GetOAISets to see what the hash looks like.
109 return unless $set_id;
111 my $dbh = C4::Context->dbh;
117 my $sth = $dbh->prepare($query);
118 $sth->execute($set_id);
119 my $set = $sth->fetchrow_hashref;
123 FROM oai_sets_descriptions
126 $sth = $dbh->prepare($query);
127 $sth->execute($set->{'id'});
128 my $desc = $sth->fetchall_arrayref({});
130 push @{$set->{'descriptions'}}, $_->{'description'};
136 =head2 GetOAISetBySpec
138 my $set = GetOAISetBySpec($setSpec);
140 Returns a hash describing the set whose spec is $setSpec
144 sub GetOAISetBySpec {
147 return unless defined $setSpec;
149 my $dbh = C4::Context->dbh;
156 my $sth = $dbh->prepare($query);
157 $sth->execute($setSpec);
159 return $sth->fetchrow_hashref;
165 'id' => $set_id, # mandatory
166 'spec' => $spec, # mandatory
167 'name' => $name, # mandatory
168 'descriptions => \@descriptions, # optional, [] to remove descriptions
172 ModOAISet modify a set in the database.
179 return unless($set && $set->{'spec'} && $set->{'name'});
181 if(!defined $set->{'id'}) {
182 warn "Set ID not defined, can't modify the set";
186 my $dbh = C4::Context->dbh;
193 my $sth = $dbh->prepare($query);
194 $sth->execute($set->{'spec'}, $set->{'name'}, $set->{'id'});
196 if($set->{'descriptions'}) {
198 DELETE FROM oai_sets_descriptions
201 $sth = $dbh->prepare($query);
202 $sth->execute($set->{'id'});
204 if(scalar @{$set->{'descriptions'}} > 0) {
206 INSERT INTO oai_sets_descriptions (set_id, description)
209 $sth = $dbh->prepare($query);
210 foreach (@{ $set->{'descriptions'} }) {
211 $sth->execute($set->{'id'}, $_) if $_;
221 DelOAISet remove the set with the given set_id
228 return unless $set_id;
230 my $dbh = C4::Context->dbh;
232 DELETE oai_sets, oai_sets_descriptions, oai_sets_mappings
234 LEFT JOIN oai_sets_descriptions ON oai_sets_descriptions.set_id = oai_sets.id
235 LEFT JOIN oai_sets_mappings ON oai_sets_mappings.set_id = oai_sets.id
236 WHERE oai_sets.id = ?
238 my $sth = $dbh->prepare($query);
239 $sth->execute($set_id);
245 'id' => $set_id, # mandatory
246 'spec' => $spec, # mandatory
247 'name' => $name, # mandatory
248 'descriptions => \@descriptions, # optional
250 my $set_id = AddOAISet($set);
252 AddOAISet adds a new set and returns its id, or undef if something went wrong.
259 return unless($set && $set->{'spec'} && $set->{'name'});
262 my $dbh = C4::Context->dbh;
264 INSERT INTO oai_sets (spec, name)
267 my $sth = $dbh->prepare($query);
268 if( $sth->execute($set->{'spec'}, $set->{'name'}) ) {
269 $set_id = $dbh->last_insert_id(undef, undef, 'oai_sets', undef);
270 if($set->{'descriptions'}) {
272 INSERT INTO oai_sets_descriptions (set_id, description)
275 $sth = $dbh->prepare($query);
276 foreach( @{ $set->{'descriptions'} } ) {
277 $sth->execute($set_id, $_) if $_;
281 warn "AddOAISet failed";
287 =head2 GetOAISetsMappings
289 my $mappings = GetOAISetsMappings;
291 GetOAISetsMappings returns mappings for all OAI Sets.
293 Mappings define how biblios are categorized in sets.
294 A mapping is defined by six properties:
297 marcfield => 'XXX', # the MARC field to check
298 marcsubfield => 'Y', # the MARC subfield to check
299 operator => 'A', # the operator 'equal' or 'notequal'; 'equal' if ''
300 marcvalue => 'zzzz', # the value to check
301 rule_operator => 'and|or|undef', # the operator between the rules
302 rule_order => 'n' # the order of the rule for the mapping
305 If defined in a set mapping, a biblio which have at least one 'Y' subfield of
306 one 'XXX' field equal to 'zzzz' will belong to this set.
308 GetOAISetsMappings returns a hashref of arrayrefs of hashrefs.
309 The first hashref keys are the sets IDs, so it looks like this:
318 rule_operator => 'and|or|undef',
332 sub GetOAISetsMappings {
333 my $dbh = C4::Context->dbh;
335 SELECT * FROM oai_sets_mappings ORDER BY set_id, rule_order
337 my $sth = $dbh->prepare($query);
341 while(my $result = $sth->fetchrow_hashref) {
342 push @{ $mappings->{$result->{'set_id'}} }, {
343 marcfield => $result->{'marcfield'},
344 marcsubfield => $result->{'marcsubfield'},
345 operator => $result->{'operator'},
346 marcvalue => $result->{'marcvalue'},
347 rule_operator => $result->{'rule_operator'},
348 rule_order => $result->{'rule_order'}
355 =head2 GetOAISetMappings
357 my $set_mappings = GetOAISetMappings($set_id);
359 Return mappings for the set with given set_id. It's an arrayref of hashrefs
363 sub GetOAISetMappings {
366 return unless $set_id;
368 my $dbh = C4::Context->dbh;
371 FROM oai_sets_mappings
375 my $sth = $dbh->prepare($query);
376 $sth->execute($set_id);
379 while(my $result = $sth->fetchrow_hashref) {
381 marcfield => $result->{'marcfield'},
382 marcsubfield => $result->{'marcsubfield'},
383 operator => $result->{'operator'},
384 marcvalue => $result->{'marcvalue'},
385 rule_operator => $result->{'rule_operator'},
386 rule_order => $result->{'rule_order'}
393 =head2 ModOAISetMappings {
404 ModOAISetMappings($set_id, $mappings);
406 ModOAISetMappings modifies mappings of a given set.
410 sub ModOAISetMappings {
411 my ($set_id, $mappings) = @_;
413 return unless $set_id;
415 my $dbh = C4::Context->dbh;
417 DELETE FROM oai_sets_mappings
420 my $sth = $dbh->prepare($query);
421 $sth->execute($set_id);
422 if(scalar @$mappings > 0) {
424 INSERT INTO oai_sets_mappings (set_id, marcfield, marcsubfield, operator, marcvalue, rule_operator, rule_order)
425 VALUES (?,?,?,?,?,?,?)
427 $sth = $dbh->prepare($query);
428 foreach (@$mappings) {
429 $sth->execute($set_id, $_->{'marcfield'}, $_->{'marcsubfield'}, $_->{'operator'}, $_->{'marcvalue'}, $_->{'rule_operator'}, $_->{'rule_order'});
434 =head2 GetOAISetsBiblio
436 $oai_sets = GetOAISetsBiblio($biblionumber);
438 Return the OAI sets where biblio appears.
440 Return value is an arrayref of hashref where each element of the array is a set.
441 Keys of hash are id, spec and name
445 sub GetOAISetsBiblio {
446 my ($biblionumber) = @_;
448 my $dbh = C4::Context->dbh;
452 LEFT JOIN oai_sets_biblios ON oai_sets_biblios.set_id = oai_sets.id
453 WHERE biblionumber = ?
455 my $sth = $dbh->prepare($query);
457 $sth->execute($biblionumber);
458 return $sth->fetchall_arrayref({});
461 =head2 DelOAISetsBiblio
463 DelOAISetsBiblio($biblionumber);
465 Remove a biblio from all sets
469 sub DelOAISetsBiblio {
470 my ($biblionumber) = @_;
472 return unless $biblionumber;
474 my $dbh = C4::Context->dbh;
476 DELETE FROM oai_sets_biblios
477 WHERE biblionumber = ?
479 my $sth = $dbh->prepare($query);
480 return $sth->execute($biblionumber);
483 =head2 CalcOAISetsBiblio
485 my @sets = CalcOAISetsBiblio($record, $oai_sets_mappings);
487 Return a list of set ids the record belongs to. $record must be a MARC::Record
488 and $oai_sets_mappings (optional) must be a hashref returned by
493 sub CalcOAISetsBiblio {
494 my ($record, $oai_sets_mappings) = @_;
496 return unless $record;
498 $oai_sets_mappings ||= GetOAISetsMappings;
501 foreach my $set_id (keys %$oai_sets_mappings) {
504 foreach my $mapping (@{ $oai_sets_mappings->{$set_id} }) {
505 next if not $mapping;
506 my $rule_operator = $mapping->{'rule_operator'};
507 my $result = _evalRule($record, $mapping);
509 # First rule or 'or' rule is always pushed
510 if (!@$rules || $rule_operator eq 'or') {
511 push @$rules, [$result];
515 # 'and' rule is pushed in the last 'or' rule
516 push @{$rules->[-1]}, $result;
520 foreach my $ruleset (@$rules) {
521 if (0 < grep /0/, @{$ruleset}) {
522 push @evaluated_and, 0;
524 push @evaluated_and, 1;
528 if (grep /1/, @evaluated_and) {
529 push @biblio_sets, $set_id;
536 # Does the record match a given mapping rule?
541 my $field = $mapping->{'marcfield'};
542 my $subfield = $mapping->{'marcsubfield'};
543 my $operator = $mapping->{'operator'};
544 my $value = $mapping->{'marcvalue'};
546 my @all_subfield_values;
547 # Get all the fields with the given tag
548 my @fields = $record->field($field);
549 # Iterate over all the fields
550 foreach my $field ( @fields ) {
551 # Get the values from all the subfields with the given subfield code
552 if ( my @subfield_values = $field->subfield($subfield) ) {
553 push @all_subfield_values, @subfield_values;
557 if ($operator eq 'notequal') {
558 if(0 == grep { $_ eq $value } @all_subfield_values) {
563 if(0 < grep { $_ eq $value } @all_subfield_values) {
571 =head2 ModOAISetsBiblios
573 my $oai_sets_biblios = {
574 '1' => [1, 3, 4], # key is the set_id, and value is an array ref of biblionumbers
578 ModOAISetsBiblios($oai_sets_biblios);
580 ModOAISetsBiblios deletes all records from oai_sets_biblios table and calls AddOAISetsBiblios.
581 This table is then used in opac/oai.pl.
585 sub ModOAISetsBiblios {
586 my $oai_sets_biblios = shift;
588 return unless ref($oai_sets_biblios) eq "HASH";
590 my $dbh = C4::Context->dbh;
592 DELETE FROM oai_sets_biblios
594 my $sth = $dbh->prepare($query);
596 AddOAISetsBiblios($oai_sets_biblios);
599 =head2 UpdateOAISetsBiblio
601 UpdateOAISetsBiblio($biblionumber, $record);
603 Update OAI sets for one biblio. The two parameters are mandatory.
604 $record is a MARC::Record.
608 sub UpdateOAISetsBiblio {
609 my ($biblionumber, $record) = @_;
611 return unless($biblionumber and $record);
613 $record = $record->clone;
614 if (C4::Context->preference('OAI-PMH:AutoUpdateSetsEmbedItemData')) {
615 $record = Koha::Biblio::Metadata->record(
619 biblionumber => $biblionumber,
625 my @sets = CalcOAISetsBiblio($record);
627 push @{ $sets_biblios->{$_} }, $biblionumber;
629 DelOAISetsBiblio($biblionumber);
630 AddOAISetsBiblios($sets_biblios);
633 =head2 AddOAISetsBiblios
635 my $oai_sets_biblios = {
636 '1' => [1, 3, 4], # key is the set_id, and value is an array ref of biblionumbers
640 ModOAISetsBiblios($oai_sets_biblios);
642 AddOAISetsBiblios insert given infos in oai_sets_biblios table.
643 This table is then used in opac/oai.pl.
647 sub AddOAISetsBiblios {
648 my $oai_sets_biblios = shift;
650 return unless ref($oai_sets_biblios) eq "HASH";
652 my $dbh = C4::Context->dbh;
654 INSERT INTO oai_sets_biblios (set_id, biblionumber)
657 my $sth = $dbh->prepare($query);
658 foreach my $set_id (keys %$oai_sets_biblios) {
659 foreach my $biblionumber (@{$oai_sets_biblios->{$set_id}}) {
660 $sth->execute($set_id, $biblionumber);