Merge remote-tracking branch 'origin/new/bug_8185'
[koha.git] / admin / matching-rules.pl
1 #! /usr/bin/perl
2 #
3 # Copyright 2007 LibLime
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #
20
21 use strict;
22 use warnings;
23
24 use CGI;
25 use C4::Auth;
26 use C4::Context;
27 use C4::Output;
28 use C4::Koha;
29 use C4::Matcher;
30
31 my $script_name = "/cgi-bin/koha/admin/matching-rules.pl";
32
33 our $input = new CGI;
34 my $op = $input->param('op') || '';
35
36
37 my ($template, $loggedinuser, $cookie)
38     = get_template_and_user({template_name => "admin/matching-rules.tmpl",
39                  query => $input,
40                  type => "intranet",
41                  authnotrequired => 0,
42                  flagsrequired => {parameters => 'parameters_remaining_permissions'},
43                  debug => 1,
44                  });
45
46 $template->param(script_name => $script_name);
47
48 my $matcher_id = $input->param("matcher_id");
49
50 $template->param(max_matchpoint => 0);
51 $template->param(max_matchcheck => 0);
52 my $display_list = 0;
53 if ($op eq "edit_matching_rule") {
54     edit_matching_rule_form($template, $matcher_id);
55 } elsif ($op eq "edit_matching_rule_confirmed") {
56     add_update_matching_rule($template, $matcher_id);
57     $display_list = 1;
58 } elsif ($op eq "add_matching_rule") {
59     add_matching_rule_form($template);
60 } elsif ($op eq "add_matching_rule_confirmed") {
61     add_update_matching_rule($template, $matcher_id);
62     $display_list = 1;
63 } elsif ($op eq "delete_matching_rule") {
64     delete_matching_rule_form($template, $matcher_id);
65 } elsif ($op eq "delete_matching_rule_confirmed") {
66     delete_matching_rule($template, $matcher_id);
67     $display_list = 1;
68 } else {
69     $display_list = 1;
70 }
71
72 if ($display_list) {
73     matching_rule_list($template);
74 }
75
76 output_html_with_http_headers $input, $cookie, $template->output;
77
78 exit 0;
79
80 sub add_matching_rule_form {
81     my $template = shift;
82
83     $template->param(
84         matching_rule_form => 1,
85         confirm_op => 'add_matching_rule_confirmed',
86         max_matchpoint => 1,
87         max_matchcheck => 1
88     );
89
90 }
91
92 sub add_update_matching_rule {
93     my $template = shift;
94     my $matcher_id = shift;
95     my $record_type = $input->param('record_type') || 'biblio';
96
97     # do parsing
98     my $matcher = C4::Matcher->new($record_type, 1000);
99     $matcher->code($input->param('code'));
100     $matcher->description($input->param('description'));
101     $matcher->threshold($input->param('threshold'));
102
103     # matchpoints
104     my @mp_nums = sort map { /^mp_(\d+)_search_index/ ? int($1): () } $input->param;
105     foreach my $mp_num (@mp_nums) {
106         my $index = $input->param("mp_${mp_num}_search_index");
107         my $score = $input->param("mp_${mp_num}_score");
108         # components
109         my $components = [];
110         my @comp_nums = sort map { /^mp_${mp_num}_c_(\d+)_tag/ ? int($1): () } $input->param;
111         foreach my $comp_num (@comp_nums) {
112             my $component = {};
113             $component->{'tag'} = $input->param("mp_${mp_num}_c_${comp_num}_tag");
114             $component->{'subfields'} = $input->param("mp_${mp_num}_c_${comp_num}_subfields");
115             $component->{'offset'} = $input->param("mp_${mp_num}_c_${comp_num}_offset");
116             $component->{'length'} = $input->param("mp_${mp_num}_c_${comp_num}_length");
117             # norms
118             $component->{'norms'} = [];
119             my @norm_nums = sort map { /^mp_${mp_num}_c_${comp_num}_n_(\d+)_norm/ ? int($1): () } $input->param;
120             foreach my $norm_num (@norm_nums) {
121                 push @{ $component->{'norms'} }, $input->param("mp_${mp_num}_c_${comp_num}_n_${norm_num}_norm");
122             }
123             push @$components, $component;
124         }
125         $matcher->add_matchpoint($index, $score, $components);
126     }
127
128     # match checks
129     my @mc_nums = sort map { /^mc_(\d+)_id/ ? int($1): () } $input->param;
130     foreach my $mc_num (@mc_nums) {
131         # source components
132         my $src_components = [];
133         my @src_comp_nums = sort map { /^mc_${mc_num}_src_c_(\d+)_tag/ ? int($1): () } $input->param;
134         foreach my $comp_num (@src_comp_nums) {
135             my $component = {};
136             $component->{'tag'} = $input->param("mc_${mc_num}_src_c_${comp_num}_tag");
137             $component->{'subfields'} = $input->param("mc_${mc_num}_src_c_${comp_num}_subfields");
138             $component->{'offset'} = $input->param("mc_${mc_num}_src_c_${comp_num}_offset");
139             $component->{'length'} = $input->param("mc_${mc_num}_src_c_${comp_num}_length");
140             # norms
141             $component->{'norms'} = [];
142             my @norm_nums = sort map { /^mc_${mc_num}_src_c_${comp_num}_n_(\d+)_norm/ ? int($1): () } $input->param;
143             foreach my $norm_num (@norm_nums) {
144                 push @{ $component->{'norms'} }, $input->param("mc_${mc_num}_src_c_${comp_num}_n_${norm_num}_norm");
145             }
146             push @$src_components, $component;
147         }
148         # target components
149         my $tgt_components = [];
150         my @tgt_comp_nums = sort map { /^mc_${mc_num}_tgt_c_(\d+)_tag/ ? int($1): () } $input->param;
151         foreach my $comp_num (@tgt_comp_nums) {
152             my $component = {};
153             $component->{'tag'} = $input->param("mc_${mc_num}_tgt_c_${comp_num}_tag");
154             $component->{'subfields'} = $input->param("mc_${mc_num}_tgt_c_${comp_num}_subfields");
155             $component->{'offset'} = $input->param("mc_${mc_num}_tgt_c_${comp_num}_offset");
156             $component->{'length'} = $input->param("mc_${mc_num}_tgt_c_${comp_num}_length");
157             # norms
158             $component->{'norms'} = [];
159             my @norm_nums = sort map { /^mc_${mc_num}_tgt_c_${comp_num}_n_(\d+)_norm/ ? int($1): () } $input->param;
160             foreach my $norm_num (@norm_nums) {
161                 push @{ $component->{'norms'} }, $input->param("mc_${mc_num}_tgt_c_${comp_num}_n_${norm_num}_norm");
162             }
163             push @$tgt_components, $component;
164         }
165         $matcher->add_required_check($src_components, $tgt_components);
166     }
167     
168     if (defined $matcher_id and $matcher_id =~ /^\d+/) {
169         $matcher->_id($matcher_id);
170         $template->param(edited_matching_rule => $matcher->code());
171     } else {
172         $template->param(added_matching_rule => $matcher->code());
173     }
174     $matcher_id = $matcher->store();
175 }
176
177 sub delete_matching_rule_form {
178     my $template = shift;
179     my $matcher_id = shift;
180
181     my $matcher = C4::Matcher->fetch($matcher_id);
182     $template->param(
183         delete_matching_rule_form => 1,
184         confirm_op => "delete_matching_rule_confirmed",
185         matcher_id => $matcher_id,
186         code => $matcher->code(),
187         description => $matcher->description(),
188     );
189 }
190
191 sub delete_matching_rule {
192     my $template = shift;
193     my $matcher_id = shift;
194
195     my $matcher = C4::Matcher->fetch($matcher_id);
196     $template->param(deleted_matching_rule => $matcher->code(),
197                     );
198     C4::Matcher->delete($matcher_id);
199 }
200
201 sub edit_matching_rule_form {
202     my $template = shift;
203     my $matcher_id = shift;
204
205     my $matcher = C4::Matcher->fetch($matcher_id);
206
207     $template->{VARS}->{'matcher_id'} = $matcher_id;
208     $template->{VARS}->{'code'} = $matcher->code();
209     $template->{VARS}->{'description'} = $matcher->description();
210     $template->{VARS}->{'threshold'} = $matcher->threshold();
211     $template->{VARS}->{'record_type'} = $matcher->record_type();
212
213     my $matcher_info = $matcher->dump();
214     my @matchpoints = ();
215     my $mp_num = 0;
216     foreach my $matchpoint (@{ $matcher_info->{'matchpoints'} }) {
217         $mp_num++;
218         my @components = _parse_components($matchpoint->{'components'});
219         push @matchpoints, { 
220             mp_num => $mp_num, 
221             index => $matchpoint->{'index'}, 
222             score => $matchpoint->{'score'},
223             components => \@components
224         };        
225     }
226     $template->param(matchpoints => \@matchpoints);
227
228     my $mc_num = 0;
229     my @matchchecks = ();
230     foreach my $matchcheck (@{ $matcher_info->{'matchchecks'} }) {
231         $mc_num++;
232         my @src_components = _parse_components($matchcheck->{'source_matchpoint'}->{'components'});
233         my @tgt_components = _parse_components($matchcheck->{'target_matchpoint'}->{'components'});
234         push @matchchecks, {
235             mc_num => $mc_num,
236             src_components => \@src_components,
237             tgt_components => \@tgt_components
238         };
239     }
240     $template->param(matchchecks => \@matchchecks);
241
242     $template->param(
243         matching_rule_form => 1,
244         edit_matching_rule => 1,
245         confirm_op => 'edit_matching_rule_confirmed',
246         max_matchpoint => $mp_num,
247         max_matchcheck => $mc_num
248     );
249
250 }
251
252 sub _parse_components {
253     my $components_ref = shift;
254     my @components = ();
255
256     my $comp_num = 0;
257     foreach my $component (@{ $components_ref  }) {
258         $comp_num++;
259         my $norm_num = 0;
260         my @norms;
261         foreach my $norm (@{ $component->{'norms'} }) {
262             $norm_num++;
263             push @norms, { norm_num => $norm_num, norm => $norm };
264         }
265         push @components, {
266             comp_num => $comp_num,
267             tag => $component->{'tag'},
268             subfields => join("", sort keys %{ $component->{'subfields'} }),
269             offset => $component->{'offset'},
270             'length' => $component->{'length'},
271             norms => \@norms
272         };
273     }
274
275     return @components;
276 }
277
278 sub matching_rule_list {
279     my $template = shift;
280     
281     my @matching_rules = C4::Matcher::GetMatcherList();
282     $template->param(available_matching_rules => \@matching_rules);
283     $template->param(display_list => 1);
284 }