@@ -, +, @@ - Apply the patch - Apply the atomicupdate - Prove t/db_dependant/OAI/AndSets.t - Check that existing mappings still work - Try modifying existing mappings - Try creating new mappings - Check that the boolean operators precedence is correctly taken into account --- C4/OAI/Sets.pm | 100 ++++++++---- admin/oai_set_mappings.pl | 13 +- installer/data/mysql/atomicupdate/bug_21520.perl | 9 + .../prog/en/modules/admin/oai_set_mappings.tt | 32 +++- t/db_dependent/OAI/AndSets.t | 181 +++++++++++++++++++++ 5 files changed, 292 insertions(+), 43 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/bug_21520.perl create mode 100644 t/db_dependent/OAI/AndSets.t --- a/C4/OAI/Sets.pm +++ a/C4/OAI/Sets.pm @@ -290,19 +290,19 @@ sub AddOAISet { GetOAISetsMappings returns mappings for all OAI Sets. Mappings define how biblios are categorized in sets. -A mapping is defined by four properties: +A mapping is defined by six properties: { - marcfield => 'XXX', # the MARC field to check - marcsubfield => 'Y', # the MARC subfield to check - operator => 'A', # the operator 'equal' or 'notequal'; 'equal' if '' - marcvalue => 'zzzz', # the value to check + marcfield => 'XXX', # the MARC field to check + marcsubfield => 'Y', # the MARC subfield to check + operator => 'A', # the operator 'equal' or 'notequal'; 'equal' if '' + marcvalue => 'zzzz', # the value to check + rule_operator => 'and|or|undef', # the operator between the rules + rule_order => 'n' # the order of the rule for the mapping } If defined in a set mapping, a biblio which have at least one 'Y' subfield of one 'XXX' field equal to 'zzzz' will belong to this set. -If multiple mappings are defined in a set, the biblio will belong to this set -if at least one condition is matched. GetOAISetsMappings returns a hashref of arrayrefs of hashrefs. The first hashref keys are the sets IDs, so it looks like this: @@ -313,7 +313,9 @@ The first hashref keys are the sets IDs, so it looks like this: marcfield => 'XXX', marcsubfield => 'Y', operator => 'A', - marcvalue => 'zzzz' + marcvalue => 'zzzz', + rule_operator => 'and|or|undef', + rule_order => 'n' }, { ... @@ -329,7 +331,7 @@ The first hashref keys are the sets IDs, so it looks like this: sub GetOAISetsMappings { my $dbh = C4::Context->dbh; my $query = qq{ - SELECT * FROM oai_sets_mappings + SELECT * FROM oai_sets_mappings ORDER BY set_id, rule_order }; my $sth = $dbh->prepare($query); $sth->execute; @@ -340,7 +342,9 @@ sub GetOAISetsMappings { marcfield => $result->{'marcfield'}, marcsubfield => $result->{'marcsubfield'}, operator => $result->{'operator'}, - marcvalue => $result->{'marcvalue'} + marcvalue => $result->{'marcvalue'}, + rule_operator => $result->{'rule_operator'}, + rule_order => $result->{'rule_order'} }; } @@ -365,6 +369,7 @@ sub GetOAISetMappings { SELECT * FROM oai_sets_mappings WHERE set_id = ? + ORDER BY rule_order }; my $sth = $dbh->prepare($query); $sth->execute($set_id); @@ -375,7 +380,9 @@ sub GetOAISetMappings { marcfield => $result->{'marcfield'}, marcsubfield => $result->{'marcsubfield'}, operator => $result->{'operator'}, - marcvalue => $result->{'marcvalue'} + marcvalue => $result->{'marcvalue'}, + rule_operator => $result->{'rule_operator'}, + rule_order => $result->{'rule_order'} }; } @@ -411,15 +418,14 @@ sub ModOAISetMappings { }; my $sth = $dbh->prepare($query); $sth->execute($set_id); - if(scalar @$mappings > 0) { $query = qq{ - INSERT INTO oai_sets_mappings (set_id, marcfield, marcsubfield, operator, marcvalue) - VALUES (?,?,?,?,?) + INSERT INTO oai_sets_mappings (set_id, marcfield, marcsubfield, operator, marcvalue, rule_operator, rule_order) + VALUES (?,?,?,?,?,?,?) }; $sth = $dbh->prepare($query); foreach (@$mappings) { - $sth->execute($set_id, $_->{'marcfield'}, $_->{'marcsubfield'}, $_->{'operator'}, $_->{'marcvalue'}); + $sth->execute($set_id, $_->{'marcfield'}, $_->{'marcsubfield'}, $_->{'operator'}, $_->{'marcvalue'}, $_->{'rule_operator'}, $_->{'rule_order'}); } } } @@ -492,30 +498,64 @@ sub CalcOAISetsBiblio { my @biblio_sets; foreach my $set_id (keys %$oai_sets_mappings) { + + my $rules = []; foreach my $mapping (@{ $oai_sets_mappings->{$set_id} }) { next if not $mapping; - my $field = $mapping->{'marcfield'}; - my $subfield = $mapping->{'marcsubfield'}; - my $operator = $mapping->{'operator'}; - my $value = $mapping->{'marcvalue'}; - my @subfield_values = $record->subfield($field, $subfield); - if ($operator eq 'notequal') { - if(0 == grep /^$value$/, @subfield_values) { - push @biblio_sets, $set_id; - last; - } + my $rule_operator = $mapping->{'rule_operator'}; + my $result = _evalRule($record, $mapping); + + # First rule or 'or' rule is always pushed + if (!@$rules || $rule_operator eq 'or') { + push @$rules, [$result]; + next; } - else { - if(0 < grep /^$value$/, @subfield_values) { - push @biblio_sets, $set_id; - last; - } + + # 'and' rule is pushed in the last 'or' rule + push @{$rules->[-1]}, $result; + } + + my @evaluated_and; + foreach my $ruleset (@$rules) { + if (0 < grep /0/, @{$ruleset}) { + push @evaluated_and, 0; + } else { + push @evaluated_and, 1; } } + + if (grep /1/, @evaluated_and) { + push @biblio_sets, $set_id; + } + } return @biblio_sets; } +# Does the record match a given mapping rule? +sub _evalRule { + my $record = shift; + my $mapping = shift; + + my $field = $mapping->{'marcfield'}; + my $subfield = $mapping->{'marcsubfield'}; + my $operator = $mapping->{'operator'}; + my $value = $mapping->{'marcvalue'}; + my @subfield_values = $record->subfield($field, $subfield); + if ($operator eq 'notequal') { + if(0 == grep /^$value$/, @subfield_values) { + return 1; + } + } + else { + if(0 < grep /^$value$/, @subfield_values) { + return 1; + } + } + return 0; +} + + =head2 ModOAISetsBiblios my $oai_sets_biblios = { --- a/admin/oai_set_mappings.pl +++ a/admin/oai_set_mappings.pl @@ -57,20 +57,25 @@ if($op && $op eq "save") { my @marcsubfields = $input->multi_param('marcsubfield'); my @operators = $input->multi_param('operator'); my @marcvalues = $input->multi_param('marcvalue'); + my @ruleoperators = $input->multi_param('rule_operator'); + my @ruleorders = $input->multi_param('rule_order'); my @mappings; my $i = 0; while($i < @marcfields and $i < @marcsubfields and $i < @marcvalues) { if($marcfields[$i] and $marcsubfields[$i]) { push @mappings, { - marcfield => $marcfields[$i], - marcsubfield => $marcsubfields[$i], - operator => $operators[$i], - marcvalue => $marcvalues[$i] + marcfield => $marcfields[$i], + marcsubfield => $marcsubfields[$i], + operator => $operators[$i], + marcvalue => $marcvalues[$i], + rule_operator => $ruleoperators[$i], + rule_order => $i }; } $i++; } + $mappings[0]{'rule_operator'} = undef if (@mappings); ModOAISetMappings($id, \@mappings); $template->param(mappings_saved => 1); } --- a/installer/data/mysql/atomicupdate/bug_21520.perl +++ a/installer/data/mysql/atomicupdate/bug_21520.perl @@ -0,0 +1,9 @@ +$DBversion = 'XXX'; # will be replaced by the RM +if( CheckVersion( $DBversion ) ) { + $dbh->do( "ALTER TABLE oai_sets_mappings ADD COLUMN rule_order INT AFTER set_id, ADD COLUMN rule_operator VARCHAR(3) AFTER rule_order" ); + $dbh->do( "UPDATE oai_sets_mappings SET rule_operator='or'" ); + + # Always end with this (adjust the bug info) + SetVersion( $DBversion ); + print "Upgrade to $DBversion done (Bug 21520 - Add rule_order and rule_operator fields to oai_sets_mappings table)\n"; +} --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/oai_set_mappings.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/oai_set_mappings.tt @@ -31,6 +31,7 @@
Rule operator | Field | Subfield | Operator | @@ -43,6 +44,12 @@ [% IF ( mappings ) %] [% FOREACH mapping IN mappings %]
---|---|---|---|
+ + |