Bugzilla – Attachment 76694 Details for
Bug 15836
Labels: Offer configuration option for splitting call numbers
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 15836: Add the ability to defined custom methods to split call number in labels
Bug-15836-Add-the-ability-to-defined-custom-method.patch (text/plain), 37.97 KB, created by
Jonathan Druart
on 2018-07-04 16:11:36 UTC
(
hide
)
Description:
Bug 15836: Add the ability to defined custom methods to split call number in labels
Filename:
MIME Type:
Creator:
Jonathan Druart
Created:
2018-07-04 16:11:36 UTC
Size:
37.97 KB
patch
obsolete
>From 9f7c053da24b7aaa191aac01758d548f818f1cad Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Thu, 21 Jun 2018 18:19:36 -0300 >Subject: [PATCH] Bug 15836: Add the ability to defined custom methods to split > call number in labels > >Currently the call number splitting seems to be mostly implemented for >DDC and LC classifications. >Those are both not very common in some countries. >A lot of libraries use their own custom classification schemes so the call number >plitting is something that should be individually configurable. > >This enhancement adds the ability to define custom splitting rules based >on regular expressions. > >How does it work so far? >From C4/Labels/Label.pm there are 3 differents splitting methods defined, depending on items.cn_source. > if cn_source is "lcc' or 'nlm' we split using Library::CallNumber::LC > if cn_source is 'ddc' we split using a in-house method >Finally there is a fallback method to split on space >And nothing else is done for other cn_source > >The idea of this patch is to mimick what was done for the "filing rules" and add >the ability to define "splitting rules" that will be used by the "Classification sources". >A classification source will then have: > * a filing rule used to sort items by callnumbers > * a splitting rule used to print labels > >To acchieve this goal this enhancement will do the following >modifications at DB level: >* New table class_split_rules >* New column class_sources.class_split_rule > >Test plan: >* Execute the update database entry to create the new table and >column. >I. UI Changes >a) Create/modify/delete a filing rule >b) Create/modify/delete a splitting rule >c) Create/modify/delete a classification source >=> A filing rule or splitting rule cannot be removed if used by a >classification source > >II. Splitting rule using regular expressions >a) Create a splitting rule using the "Splitting routine" "RegEx" >b) Define several regular expressions, they will be applied one after >the other in the same order you define them. >Something like: > s/\s/\n/g # Break on spaces > s/(\s?=)/\n=/g # Break on = (unless it's done already) > s/^(J|K)\n/$1 / # Remove the first break if callnumber starts with J or K >c) You can test the regular expressions using filling the textarea with >a list of callnumbers. Then click "Test" and confirm the callnumbers are >split how you expected. >d) Finally create a new classification source that will use this new >splitting rule. > >III. Print the label! >a) Create a layout. It should have the "Split call numbers" checkbox >ticked, and display itemcallnumber >b) Use this layout to export labels, use items with different >classification source ('lcc', 'ddc', but also the new one you have >create) >=> The callnumbers should have been split according to the regex you >defined earlier! > >Notes: >* The update database entry fill the class_sources.class_split_rule >with the value of class_sources.class_sort_rule >If default rules exist it will not work, we should add a note in the >release notes (would be enough?) >* C4::ClassSplitRoutine::* should be moved to Koha::ClassSplitRule, >but it sounded better to keep the same pattern as ClassSortRoutines >* Should not we use a LONGTEXT for class_split_rules.split_regex instead >of VARCHAR(255)? > >* class_sources.sql should be filled for other languages before pushed >to master! > >IMPORTANT NOTES: The regular expressions are stored as it, and eval is >used to evaluate it (perlcritic raises a warning about it (Expression >form of "eval"). It can lead to serious security issues (execution of >arbitrary code on the server), especially if the modifier 'e' is used. >We could then remedy the situation with one of these following points: >- Assume that this DB data is safe (We can add a new permission?) >- Assume that the data is not safe and deal with possible attack >Cons: how be sure we are exhaustive? Making sure it matches ^s///[^e/]*$ >would be enough? >- Use Template Toolkit syntax instead (Really safer?) > [% callnumber.replace('\s', '\n').replace ... %] >- Cut the regex parts: find, replace, modifiers >like we already do for Marc modification template. Cons: we are going to >have escape problems, the "find" and "replace" parts should not be >handle the same way (think "\n", "\\n", "\1", "\s", etc.) >I did not manage to implement this one easily. >--- > C4/ClassSplitRoutine.pm | 64 +++++++++++++ > C4/ClassSplitRoutine/Dewey.pm | 80 +++++++++++++++++ > C4/ClassSplitRoutine/Generic.pm | 73 +++++++++++++++ > C4/ClassSplitRoutine/LCC.pm | 64 +++++++++++++ > C4/ClassSplitRoutine/RegEx.pm | 60 +++++++++++++ > C4/Labels/Label.pm | 100 ++++++--------------- > Koha/ClassSplitRule.pm | 78 ++++++++++++++++ > Koha/ClassSplitRules.pm | 56 ++++++++++++ > admin/classsources.pl | 23 +++-- > .../prog/en/modules/admin/admin-home.tt | 2 +- > .../prog/en/modules/admin/classsources.tt | 90 +++++++++++++++++-- > t/Labels.t | 3 +- > t/Labels_split_Regex.t | 38 ++++++++ > t/Labels_split_ddcn.t | 5 +- > t/Labels_split_lccn.t | 3 +- > 15 files changed, 643 insertions(+), 96 deletions(-) > create mode 100644 C4/ClassSplitRoutine.pm > create mode 100644 C4/ClassSplitRoutine/Dewey.pm > create mode 100644 C4/ClassSplitRoutine/Generic.pm > create mode 100644 C4/ClassSplitRoutine/LCC.pm > create mode 100644 C4/ClassSplitRoutine/RegEx.pm > create mode 100644 Koha/ClassSplitRule.pm > create mode 100644 Koha/ClassSplitRules.pm > create mode 100755 t/Labels_split_Regex.t > >diff --git a/C4/ClassSplitRoutine.pm b/C4/ClassSplitRoutine.pm >new file mode 100644 >index 0000000000..ee7ec478eb >--- /dev/null >+++ b/C4/ClassSplitRoutine.pm >@@ -0,0 +1,64 @@ >+package C4::ClassSplitRoutine; >+ >+# Copyright 2018 Koha Development Team >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+require Exporter; >+use Class::Factory::Util; >+ >+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); >+ >+ >+=head1 NAME >+ >+C4::ClassSplitRoutine - base object for creation of classification splitting routines >+ >+=head1 SYNOPSIS >+ >+use C4::ClassSplitRoutine; >+ >+=head1 FUNCTIONS >+ >+=cut >+ >+@ISA = qw(Exporter); >+@EXPORT = qw( >+ &GetSplitRoutineNames >+); >+ >+=head2 GetSplitRoutineNames >+ >+ my @routines = GetSplitRoutineNames(); >+ >+Get names of all modules under C4::ClassSplitRoutine::*. >+ >+=cut >+ >+sub GetSplitRoutineNames { >+ return C4::ClassSplitRoutine->subclasses(); >+} >+ >+1; >+ >+=head1 AUTHOR >+ >+Koha Development Team <http://koha-community.org/> >+ >+=cut >+ >diff --git a/C4/ClassSplitRoutine/Dewey.pm b/C4/ClassSplitRoutine/Dewey.pm >new file mode 100644 >index 0000000000..10d20f11dd >--- /dev/null >+++ b/C4/ClassSplitRoutine/Dewey.pm >@@ -0,0 +1,80 @@ >+package C4::ClassSplitRoutine::Dewey; >+ >+# Copyright 2018 Koha Development Team >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use C4::Debug; >+ >+=head1 NAME >+ >+C4::ClassSplitRoutine::Dewey - Dewey call number split method >+ >+=head1 SYNOPSIS >+ >+use C4::ClassSplitRoutine; >+ >+my $cn_split = C4::ClassSplitRoutine::Dewey::split_callnumber($cn_item); >+ >+=head1 FUNCTIONS >+ >+=head2 split_callnumber >+ >+ my $cn_split = C4::ClassSplitRoutine::Dewey::split_callnumber($cn_item); >+ >+=cut >+ >+sub split_callnumber { >+ my ($cn_item) = @_; >+ >+ my $possible_decimal = >+ qr/\d{3,}(?:\.\d+)?/; # at least three digits for a DDCN >+ >+ $cn_item =~ s/\///g >+ ; # in theory we should be able to simply remove all segmentation markers and arrive at the correct call number... >+ my (@lines) = $cn_item =~ m/ >+ ^([-a-zA-Z]*\s?(?:$possible_decimal)?) # R220.3 CD-ROM 787.87 # will require extra splitting >+ \s+ >+ (.+) # H2793Z H32 c.2 EAS # everything else (except bracketing spaces) >+ \s* >+ /x; >+ unless (@lines) { >+ warn sprintf( 'regexp failed to match string: %s', $cn_item ); >+ push @lines, $cn_item; # if no match, just push the whole string. >+ } >+ >+ if ( $lines[0] =~ /^([-a-zA-Z]+)\s?($possible_decimal)$/ ) { >+ shift @lines; # pull off the mathching first element, like example 1 >+ unshift @lines, $1, $2; # replace it with the two pieces >+ } >+ >+ push @lines, split /\s+/, >+ pop @lines >+ ; # split the last piece into an arbitrary number of pieces at spaces >+ $debug and print STDERR "split_ddcn array: ", join( " | ", @lines ), "\n"; >+ return @lines; >+} >+ >+1; >+ >+=head1 AUTHOR >+ >+Koha Development Team <http://koha-community.org/> >+ >+=cut >+ >diff --git a/C4/ClassSplitRoutine/Generic.pm b/C4/ClassSplitRoutine/Generic.pm >new file mode 100644 >index 0000000000..ca9b1754f1 >--- /dev/null >+++ b/C4/ClassSplitRoutine/Generic.pm >@@ -0,0 +1,73 @@ >+package C4::ClassSplitRoutine::Generic; >+ >+# Copyright 2018 Koha Development Team >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use C4::Debug; >+ >+=head1 NAME >+ >+C4::ClassSplitRoutine::Generic - generic call number sorting key routine >+ >+=head1 SYNOPSIS >+ >+use C4::ClassSplitRoutine; >+ >+=head1 FUNCTIONS >+ >+ >+=head2 split_callnumber >+ >+ my $cn_split = C4::ClassSplitRoutine::Generic::split_callnumber($cn_item); >+ >+ NOTE: Custom call number types go here. It may be necessary to create additional >+ splitting algorithms if some custom call numbers cannot be made to work here. >+ Presently this splits standard non-ddcn, non-lccn fiction and biography call numbers. >+ >+=cut >+ >+sub split_callnumber { >+ my ($cn_item) = @_; >+ >+ my @lines; >+ >+ # Split call numbers based on spaces >+ push @lines, split /\s+/, $cn_item >+ ; # split the call number into an arbitrary number of pieces at spaces >+ if ( $lines[-1] !~ /^.*\d-\d.*$/ && $lines[-1] =~ /^(.*\d+)(\D.*)$/ ) { >+ pop @lines; # pull off the matching last element >+ push @lines, $1, $2; # replace it with the two pieces >+ } >+ unless ( scalar @lines ) { >+ warn sprintf( 'regexp failed to match string: %s', $cn_item ); >+ push( @lines, $cn_item ); >+ } >+ $debug and print STDERR "split_ccn array: ", join( " | ", @lines ), "\n"; >+ >+ return @lines; >+} >+ >+1; >+ >+=head1 AUTHOR >+ >+Koha Development Team <http://koha-community.org/> >+ >+=cut >+ >diff --git a/C4/ClassSplitRoutine/LCC.pm b/C4/ClassSplitRoutine/LCC.pm >new file mode 100644 >index 0000000000..8f1a7740a9 >--- /dev/null >+++ b/C4/ClassSplitRoutine/LCC.pm >@@ -0,0 +1,64 @@ >+package C4::ClassSplitRoutine::LCC; >+ >+# Copyright 2018 Koha Development Team >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+use Library::CallNumber::LC; >+ >+use C4::Debug; >+ >+=head1 NAME >+ >+C4::ClassSplitRoutine::LCC - LCC call number split method >+ >+=head1 SYNOPSIS >+ >+use C4::ClassSplitRoutine; >+ >+my $cn_split = C4::ClassSplitRoutine::LCC::split_callnumber($cn_item); >+ >+=head1 FUNCTIONS >+ >+=head2 split_callnumber >+ >+ my $cn_split = C4::ClassSplitRoutine::LCC::split_callnumber($cn_item); >+ >+=cut >+ >+sub split_callnumber { >+ my ($cn_item) = @_; >+ # lccn examples: 'HE8700.7 .P6T44 1983', 'BS2545.E8 H39 1996'; >+ my @lines = Library::CallNumber::LC->new($cn_item)->components(); >+ unless (scalar @lines && defined $lines[0]) { >+ $debug and warn sprintf('regexp failed to match string: %s', $cn_item); >+ @lines = $cn_item; # if no match, just use the whole string. >+ } >+ my $LastPiece = pop @lines; >+ push @lines, split /\s+/, $LastPiece if $LastPiece; # split the last piece into an arbitrary number of pieces at spaces >+ $debug and warn "split LCC array: ", join(" | ", @lines), "\n"; >+ return @lines; >+} >+ >+1; >+ >+=head1 AUTHOR >+ >+Koha Development Team <http://koha-community.org/> >+ >+=cut >+ >diff --git a/C4/ClassSplitRoutine/RegEx.pm b/C4/ClassSplitRoutine/RegEx.pm >new file mode 100644 >index 0000000000..a33f5dcbdb >--- /dev/null >+++ b/C4/ClassSplitRoutine/RegEx.pm >@@ -0,0 +1,60 @@ >+package C4::ClassSplitRoutine::RegEx; >+ >+# Copyright 2018 Koha Development Team >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use C4::Debug; >+ >+=head1 NAME >+ >+C4::ClassSplitRoutine::RegEx - regex call number sorting key routine >+ >+=head1 SYNOPSIS >+ >+use C4::ClassSplitRoutine; >+ >+my $cn_sort = C4::ClassSplitRoutine::RegEx::split_callnumber($cn_item, $regexs); >+ >+=head1 FUNCTIONS >+ >+=head2 split_callnumber >+ >+ my $cn_split = C4::ClassSplitRoutine::RegEx::split_callnumber($cn_item, $regexs); >+ >+=cut >+ >+sub split_callnumber { >+ my ($cn_item, $regexs) = @_; >+ >+ for my $regex ( @$regexs ) { >+ eval "\$cn_item =~ $regex"; >+ } >+ my @lines = split "\n", $cn_item; >+ >+ return @lines; >+} >+ >+1; >+ >+=head1 AUTHOR >+ >+Koha Development Team <http://koha-community.org/> >+ >+=cut >+ >diff --git a/C4/Labels/Label.pm b/C4/Labels/Label.pm >index 8fe725fece..21a178f72d 100644 >--- a/C4/Labels/Label.pm >+++ b/C4/Labels/Label.pm >@@ -7,15 +7,18 @@ use Text::Wrap; > use Algorithm::CheckDigits; > use Text::CSV_XS; > use Data::Dumper; >-use Library::CallNumber::LC; > use Text::Bidi qw( log2vis ); > > use C4::Context; > use C4::Debug; > use C4::Biblio; >- >- >-my $possible_decimal = qr/\d{3,}(?:\.\d+)?/; # at least three digits for a DDCN >+use Koha::ClassSources; >+use Koha::ClassSortRules; >+use Koha::ClassSplitRules; >+use C4::ClassSplitRoutine::Dewey; >+use C4::ClassSplitRoutine::LCC; >+use C4::ClassSplitRoutine::Generic; >+use C4::ClassSplitRoutine::RegEx; > > sub _check_params { > my $given_params = {}; >@@ -121,67 +124,6 @@ sub _get_text_fields { > return \@sorted_fields; > } > >- >-sub _split_lccn { >- my ($lccn) = @_; >- $_ = $lccn; >- # lccn examples: 'HE8700.7 .P6T44 1983', 'BS2545.E8 H39 1996'; >- my @parts = Library::CallNumber::LC->new($lccn)->components(); >- unless (scalar @parts && defined $parts[0]) { >- $debug and warn sprintf('regexp failed to match string: %s', $_); >- @parts = $_; # if no match, just use the whole string. >- } >- my $LastPiece = pop @parts; >- push @parts, split /\s+/, $LastPiece if $LastPiece; # split the last piece into an arbitrary number of pieces at spaces >- $debug and warn "split_lccn array: ", join(" | ", @parts), "\n"; >- return @parts; >-} >- >-sub _split_ddcn { >- my ($ddcn) = @_; >- $_ = $ddcn; >- s/\///g; # in theory we should be able to simply remove all segmentation markers and arrive at the correct call number... >- my (@parts) = m/ >- ^([-a-zA-Z]*\s?(?:$possible_decimal)?) # R220.3 CD-ROM 787.87 # will require extra splitting >- \s+ >- (.+) # H2793Z H32 c.2 EAS # everything else (except bracketing spaces) >- \s* >- /x; >- unless (scalar @parts) { >- warn sprintf('regexp failed to match string: %s', $_); >- push @parts, $_; # if no match, just push the whole string. >- } >- >- if ($parts[0] =~ /^([-a-zA-Z]+)\s?($possible_decimal)$/) { >- shift @parts; # pull off the mathching first element, like example 1 >- unshift @parts, $1, $2; # replace it with the two pieces >- } >- >- push @parts, split /\s+/, pop @parts; # split the last piece into an arbitrary number of pieces at spaces >- $debug and print STDERR "split_ddcn array: ", join(" | ", @parts), "\n"; >- return @parts; >-} >- >-## NOTE: Custom call number types go here. It may be necessary to create additional splitting algorithms if some custom call numbers >-## cannot be made to work here. Presently this splits standard non-ddcn, non-lccn fiction and biography call numbers. >- >-sub _split_ccn { >- my ($fcn) = @_; >- my @parts = (); >- # Split call numbers based on spaces >- push @parts, split /\s+/, $fcn; # split the call number into an arbitrary number of pieces at spaces >- if ($parts[-1] !~ /^.*\d-\d.*$/ && $parts[-1] =~ /^(.*\d+)(\D.*)$/) { >- pop @parts; # pull off the matching last element >- push @parts, $1, $2; # replace it with the two pieces >- } >- unless (scalar @parts) { >- warn sprintf('regexp failed to match string: %s', $_); >- push (@parts, $_); >- } >- $debug and print STDERR "split_ccn array: ", join(" | ", @parts), "\n"; >- return @parts; >-} >- > sub _get_barcode_data { > my ( $f, $item, $record ) = @_; > my $kohatables = _desc_koha_tables(); >@@ -400,6 +342,14 @@ sub draw_label_text { > # FIXME - returns all items, so you can't get data from an embedded holdings field. > # TODO - add a GetMarcBiblio1item(bibnum,itemnum) or a GetMarcItem(itemnum). > my $cn_source = ($item->{'cn_source'} ? $item->{'cn_source'} : C4::Context->preference('DefaultClassificationSource')); >+ my $class_source = Koha::ClassSources->find( $cn_source ); >+ my ( $split_routine, $regexs ); >+ if ($class_source) { >+ my $class_split_rule = Koha::ClassSplitRules->find( $class_source->class_split_rule ); >+ $split_routine = $class_split_rule->split_routine; >+ $regexs = $class_split_rule->regexs; >+ } >+ else { $split_routine = $cn_source } > LABEL_FIELDS: # process data for requested fields on current label > for my $field (@$label_fields) { > if ($field->{'code'} eq 'itemtype') { >@@ -429,14 +379,18 @@ sub draw_label_text { > # Fields which hold call number data FIXME: ( 060? 090? 092? 099? ) > my @callnumber_list = qw(itemcallnumber 050a 050b 082a 952o 995k); > if ((grep {$field->{'code'} =~ m/$_/} @callnumber_list) and ($self->{'printing_type'} eq 'BIB') and ($self->{'callnum_split'})) { # If the field contains the call number, we do some sp >- if ($cn_source eq 'lcc' || $cn_source eq 'nlm') { # NLM and LCC should be split the same way >- @label_lines = _split_lccn($field_data); >- @label_lines = _split_ccn($field_data) if !@label_lines; # If it was not a true lccn, try it as a custom call number >- push (@label_lines, $field_data) if !@label_lines; # If it was not that, send it on unsplit >- } elsif ($cn_source eq 'ddc') { >- @label_lines = _split_ddcn($field_data); >- @label_lines = _split_ccn($field_data) if !@label_lines; >- push (@label_lines, $field_data) if !@label_lines; >+ if ($split_routine eq 'LCC' || $split_routine eq 'nlm') { # NLM and LCC should be split the same way >+ @label_lines = C4::ClassSplitRoutine::LCC::split_callnumber($field_data); >+ @label_lines = C4::ClassSplitRoutine::Generic::split_callnumber($field_data) unless @label_lines; # If it was not a true lccn, try it as a custom call number >+ push (@label_lines, $field_data) unless @label_lines; # If it was not that, send it on unsplit >+ } elsif ($split_routine eq 'Dewey') { >+ @label_lines = C4::ClassSplitRoutine::Dewey::split_callnumber($field_data); >+ @label_lines = C4::ClassSplitRoutine::Generic::split_callnumber($field_data) unless @label_lines; >+ push (@label_lines, $field_data) unless @label_lines; >+ } elsif ($split_routine eq 'RegEx' ) { >+ @label_lines = C4::ClassSplitRoutine::RegEx::split_callnumber($field_data, $regexs); >+ @label_lines = C4::ClassSplitRoutine::Generic::split_callnumber($field_data) unless @label_lines; >+ push (@label_lines, $field_data) unless @label_lines; > } else { > warn sprintf('Call number splitting failed for: %s. Please add this call number to bug #2500 at bugs.koha-community.org', $field_data); > push @label_lines, $field_data; >diff --git a/Koha/ClassSplitRule.pm b/Koha/ClassSplitRule.pm >new file mode 100644 >index 0000000000..0505120abb >--- /dev/null >+++ b/Koha/ClassSplitRule.pm >@@ -0,0 +1,78 @@ >+package Koha::ClassSplitRule; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use JSON qw(to_json from_json); >+ >+use Koha::Database; >+ >+use base qw(Koha::Object); >+ >+=head1 NAME >+ >+Koha::ClassSplitRule Koha Classfication Spliting Rule Object class >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 new >+ >+Accept 'regexs' as a valid attribute. >+It should be an arrayref that will be serialized in JSON before stored in DB. >+ >+=cut >+ >+sub new { >+ my ($class, $attributes) = @_; >+ >+ if ( exists $attributes->{regexs} ) { >+ $attributes->{split_regex} = to_json($attributes->{regexs}); >+ delete $attributes->{regexs}; >+ } >+ return $class->SUPER::new($attributes); >+} >+ >+=head3 regexs >+ >+my $regexs = $rule->regexs >+ >+$rule->regex(\@regexs); >+ >+Getter or setter for split_regex >+ >+=cut >+ >+sub regexs { >+ my ( $self, $regexs ) = @_; >+ return $regexs >+ ? $self->split_regex( to_json($regexs) ) >+ : from_json( $self->split_regex ); >+} >+ >+=head3 type >+ >+=cut >+ >+sub _type { >+ return 'ClassSplitRule'; >+} >+ >+1; >diff --git a/Koha/ClassSplitRules.pm b/Koha/ClassSplitRules.pm >new file mode 100644 >index 0000000000..7cabff047f >--- /dev/null >+++ b/Koha/ClassSplitRules.pm >@@ -0,0 +1,56 @@ >+package Koha::ClassSplitRules; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Koha::Database; >+ >+use Koha::ClassSplitRule; >+ >+use base qw(Koha::Objects); >+ >+=head1 NAME >+ >+Koha::ClassSplitRules - Koha Classification Split Rules Object set class >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 _type >+ >+Returns name of corresponding DBIC resultset >+ >+=cut >+ >+sub _type { >+ return 'ClassSplitRule'; >+} >+ >+=head3 object_class >+ >+Returns name of corresponding Koha object class >+ >+=cut >+ >+sub object_class { >+ return 'Koha::ClassSplitRule'; >+} >+ >+1; >diff --git a/admin/classsources.pl b/admin/classsources.pl >index 24a257034a..d6576ac4e0 100755 >--- a/admin/classsources.pl >+++ b/admin/classsources.pl >@@ -27,6 +27,7 @@ use C4::Output; > use C4::Koha; > use C4::ClassSource; > use C4::ClassSortRoutine; >+use C4::ClassSplitRoutine; > use Koha::ClassSources; > use Koha::ClassSortRules; > use Koha::ClassSplitRules; >@@ -40,7 +41,7 @@ my $class_sort_rule = $input->param('class_sort_rule'); > my $class_split_rule = $input->param('class_split_rule'); > my $sort_routine = $input->param('sort_routine'); > my $split_routine = $input->param('split_routine'); >-my $split_regex = $input->param('split_regex'); >+my @split_regex = $input->multi_param('split_regex'); > my $description = $input->param('description'); > my $used = $input->param('used'); > >@@ -194,16 +195,20 @@ elsif ( $op eq "add_split_rule" ) { > } > elsif ( $op eq "add_split_rule_validate" ) { > my $split_rule = Koha::ClassSplitRules->find($class_split_rule); >+ >+ @split_regex = grep {!/^$/} @split_regex; # Remove empty > if ($split_rule) { > $split_rule->set( > { > description => $description, > split_routine => $split_routine, >- split_regex => >- ( $split_routine eq 'RegEx' ? $split_regex : '' ), > } > ); >- eval { $split_rule->store; }; >+ eval { >+ $split_rule->regexs(\@split_regex) >+ if $split_routine eq 'RegEx'; >+ $split_rule->store; >+ }; > if ($@) { > push @messages, > { type => 'error', code => 'error_on_update_split_rule' }; >@@ -220,8 +225,7 @@ elsif ( $op eq "add_split_rule_validate" ) { > class_split_rule => $class_split_rule, > description => $description, > split_routine => $split_routine, >- split_regex => >- ( $split_routine eq 'RegEx' ? $split_regex : '' ), >+ regexs => \@split_regex, > } > ); > eval { $split_rule->store; }; >@@ -273,11 +277,6 @@ sub get_class_sort_routines { > } > > sub get_class_split_routines { >- my @split_routines = qw( >- Dewey >- Generic >- LCC >- RegEx >- ); >+ my @split_routines = GetSplitRoutineNames(); > return \@split_routines; > } >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt >index c0c1917691..b9db1665b9 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt >@@ -82,7 +82,7 @@ > <dt><a href="/cgi-bin/koha/admin/authtypes.pl">Authority types</a></dt> > <dd>Create and manage Authorities frameworks that define the characteristics of your MARC Records (field and subfield definitions).</dd> > <dt><a href="/cgi-bin/koha/admin/classsources.pl">Classification sources</a></dt> >- <dd>Define classification sources (i.e., call number schemes) used by your collection. Also define filing rules used for sorting call numbers.</dd> >+ <dd>Define classification sources (i.e., call number schemes) used by your collection. Also define filing rules used for sorting call numbers and splitting rules for splitting them.</dd> > <dt><a href="/cgi-bin/koha/admin/matching-rules.pl">Record matching rules</a></dt> > <dd>Manage rules for automatically matching MARC records during record imports.</dd> > <dt><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></dt> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/classsources.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/classsources.tt >index e8844fb172..e631525c02 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/classsources.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/classsources.tt >@@ -15,6 +15,14 @@ > > </title> > [% INCLUDE 'doc-head-close.inc' %] >+<style type="text/css"> >+ #button_add, >+ .split_regex, >+ .split_callnumber >+ { >+ padding-left: 10em; >+ } >+</style> > </head> > > <body id="admin_classsources" class="admin"> >@@ -232,12 +240,33 @@ > </select> > <span class="required">Required</span> > </li> >- <li id="regex_block"> >- <label for="split_regex" class="required">Regular expression: </label> >- <input type="text" name="split_regex" id="split_regex" value="[% split_rule.split_regex %]" /> >- <span class="required">Required</span> >- </li> >- >+ <li id="regex_block"> >+ <label for="split_regex" class="required">Regular expression: </label> >+ <div> >+ [% IF split_rule %] >+ [% FOR re IN split_rule.regexs %] >+ <div class="split_regex"> >+ <input type="text" name="split_regex" id="split_regex" value="[% re | html %]" /> >+ <a href="#" title="Delete" class="del_regex"><i class="fa fa-fw fa-trash"></i>Delete</a> >+ </div> >+ [% END %] >+ [% ELSE %] >+ <div class="split_regex"> >+ <input type="text" name="split_regex" id="split_regex" value="" /> >+ </div> >+ [% END %] >+ </div> >+ <div id="button_add"> >+ <a href="#" class="add_regex" title="Add a new regular expression"><i class="fa fa-fw fa-plus"></i> New</a> >+ <span class="required">Required</span> >+ </div> >+ <label for="callnumbers">Test the regular expressions:</label> >+ <div id="test_split"> >+ <textarea name="callnumbers"></textarea> >+ <button name="test_split_cns">Test</button> >+ <div id="result_split_callnumbers"></div> >+ </div> >+ </li> > </ol> > </fieldset> > <fieldset class="action"> >@@ -346,6 +375,26 @@ > $("#split_regex").removeAttr('required'); > } > }; >+ function update_delete_links_visibility() { >+ if( $("div.split_regex").length > 1 ) { >+ $("div.split_regex").find('a.del_regex').show(); >+ } else { >+ $("div.split_regex").find('a.del_regex').hide(); >+ } >+ } >+ >+ function add_regex() { >+ var div_node = $("div.split_regex:last"); >+ var div_clone = $(div_node).clone(true); >+ $(div_clone).find("input").val(""); >+ $(div_node).after(div_clone); >+ update_delete_links_visibility(); >+ } >+ function del_regex(a_node) { >+ $(a_node).parent('div').remove(); >+ update_delete_links_visibility(); >+ } >+ > $(document).ready(function(){ > update_regex_block(); > $("#split_routine").on("change", function(){ >@@ -361,6 +410,35 @@ > $(".delete_split_rule").on("click", function(e){ > return confirm(_("Are you sure you want to delete this splitting rule?")); > }); >+ >+ $(".add_regex").on("click", function(e){ >+ e.preventDefault(); >+ add_regex(); >+ }); >+ $(".del_regex").on("click", function(e){ >+ e.preventDefault(); >+ del_regex(this); >+ }); >+ >+ $("button[name='test_split_cns']").on("click", function(e){ >+ e.preventDefault(); >+ var regexs = []; >+ $("input[name='split_regex']").each(function(){ >+ regexs.push($(this).val()); >+ }); >+ var callnumbers = $("textarea[name='callnumbers']").val(); >+ $.getJSON('/cgi-bin/koha/svc/split_callnumbers', {regexs: JSON.stringify(regexs), callnumbers: callnumbers}, function(answer){ >+ var callnumbers = answer['split_callnumbers']; >+ $("#result_split_callnumbers").html(''); >+ var list_node = $('<ol></ol>'); >+ $(callnumbers).each(function(){ >+ var split_cn = this['split'].join('<br/>'); >+ var li_node = $('<li><label>'+this['inline']+'</label><div class="split_callnumber">'+split_cn+'</div></li>'); >+ $(list_node).append(li_node); >+ }); >+ $("#result_split_callnumbers").append(list_node); >+ }).fail(function(){alert("Something went wrong.")}); >+ }); > }); > > </script> >diff --git a/t/Labels.t b/t/Labels.t >index 1eac116bb9..bd0d9ec85b 100644 >--- a/t/Labels.t >+++ b/t/Labels.t >@@ -20,6 +20,7 @@ > use strict; > use warnings; > >+use C4::ClassSplitRoutine::LCC; > use Test::More tests => 11; > > BEGIN { >@@ -62,4 +63,4 @@ Q > > ok(C4::Labels::Label::_get_text_fields(), 'test getting textx fields'); > >-is(C4::Labels::Label::_split_lccn(),"0", 'test when _split_lccn is null'); >+is(C4::ClassSplitRoutine::LCC::split_callnumber(),"0", 'test when split LCC is null'); >diff --git a/t/Labels_split_Regex.t b/t/Labels_split_Regex.t >new file mode 100755 >index 0000000000..e94eaec170 >--- /dev/null >+++ b/t/Labels_split_Regex.t >@@ -0,0 +1,38 @@ >+#!/usr/bin/perl >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::More tests => 5; >+use C4::ClassSplitRoutine::RegEx; >+ >+my $callnumbers = { >+ '830 Han' => [qw{830 Han}], >+ '159.9 (091) Gesh' => [qw{159.9 (091) Gesh}], >+ 'J 3 Kin =774' => ['J 3', 'Kin', '=774'], >+ '830 Hil =774 4' => [qw{830 Hil =774 4}], >+ '830 Las=20 4' => [qw{830 Las =20 4}], >+}; >+ >+# Split on spaces and before = >+# If starts with J or K then do not split the first 2 groups >+my @regexs = ('s/\s/\n/g', 's/(\s?=)/\n=/g', 's/^(J|K)\n/$1 /'); >+foreach my $cn (sort keys %$callnumbers) { >+ my @parts = C4::ClassSplitRoutine::RegEx::split_callnumber($cn, \@regexs); >+ my @expected = @{$callnumbers->{$cn}}; >+ is_deeply(\@parts, \@expected); >+} >diff --git a/t/Labels_split_ddcn.t b/t/Labels_split_ddcn.t >index 7579de23d3..33edba19ca 100755 >--- a/t/Labels_split_ddcn.t >+++ b/t/Labels_split_ddcn.t >@@ -42,7 +42,7 @@ BEGIN { > $test_num += 4; > } > plan tests => $test_num; >- use_ok('C4::Labels::Label'); >+ use_ok('C4::ClassSplitRoutine::Dewey'); > use vars qw($ddcns); > } > >@@ -50,7 +50,8 @@ foreach my $ddcn (sort keys %$ddcns) { > my (@parts, @expected); > ok($ddcn, "ddcn: $ddcn"); > ok(@expected = @{$ddcns->{$ddcn}}, "split expected to produce " . scalar(@expected) . " pieces"); >- ok(@parts = C4::Labels::Label::_split_ddcn($ddcn), "C4::Labels::Label::_split_ddcn($ddcn)"); >+ ok(@parts = C4::ClassSplitRoutine::Dewey::split_callnumber($ddcn), "Dewey::split_callnumber($ddcn)"); >+ > ok(scalar(@expected) == scalar(@parts), sprintf("%d of %d pieces produced", scalar(@parts), scalar(@expected))); > my $i = 0; > foreach my $unit (@expected) { >diff --git a/t/Labels_split_lccn.t b/t/Labels_split_lccn.t >index 4034e2105f..f9462ba23c 100755 >--- a/t/Labels_split_lccn.t >+++ b/t/Labels_split_lccn.t >@@ -20,6 +20,7 @@ > use strict; > use warnings; > >+use C4::ClassSplitRoutine::LCC; > use Test::More; > > BEGIN { >@@ -52,7 +53,7 @@ foreach my $lccn (sort keys %$lccns) { > my (@parts, @expected); > ok($lccn, "lccn: $lccn"); > ok(@expected = @{$lccns->{$lccn}}, "split expected to produce " . scalar(@expected) . " pieces"); >- ok(@parts = C4::Labels::Label::_split_lccn($lccn), "C4::Labels::Label::_split_lccn($lccn)"); >+ ok(@parts = C4::ClassSplitRoutine::LCC::split_callnumber($lccn), "split LCC ($lccn)"); > ok(scalar(@expected) == scalar(@parts), sprintf("%d of %d pieces produced", scalar(@parts), scalar(@expected))); > my $i = 0; > foreach my $unit (@expected) { >-- >2.11.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 15836
:
76691
|
76692
|
76693
|
76694
|
76695
|
76696
|
77061
|
77079
|
77306
|
78791
|
78793
|
78795
|
78796
|
78798
|
78799
|
79029
|
80339
|
80340
|
80341
|
80342
|
80343
|
80344
|
80345
|
80346
|
80347
|
80548
|
80549
|
80550
|
80551
|
80552
|
80553
|
80554
|
80555
|
80556
|
81033
|
81034
|
81035
|
81036
|
81037
|
81038
|
81039
|
81040
|
81041
|
81627
|
81628
|
81629
|
81630
|
81631
|
81632
|
81633
|
81634
|
81635
|
82110
|
82112
|
82113
|
82118
|
82119
|
82120
|
82289
|
82290
|
82291
|
82292
|
82293
|
82294
|
82295
|
82296
|
82297
|
82298
|
82299
|
82300
|
82301
|
82302
|
82317
|
82318
|
82319
|
82321