3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
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
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.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
20 #-----------------------------------
21 # Script Name: zed-koha-server.pl
22 # Script Version: 0.02
24 # Author: Joshua Ferraro [jmf at kados dot org]
25 # Description: A very basic Z3950 Server
26 # Usage: zed-koha-server.pl
28 # 0.00 2003/08/14: Original version; search works.
29 # 0.01 2003/10/02: First functional version; search and fetch working
30 # records returned in USMARC (ISO2709) format,
31 # Bath compliant to Level 1 in Functional Areas A, B.
32 # 0.02 2004/04/14: Cleaned up documentation, etc. No functional
34 #-----------------------------------
35 # Note: After installing SimpleServer (indexdata.dk/simpleserver) and
36 # changing the leader information in Koha's MARCgetbiblio subroutine in
37 # Biblio.pm you can run this script as root:
39 # ./zed-koha-server.pl
41 # and the server will start running on port 9999 and will allow searching
42 # and retrieval of records in MARC21 (USMARC; ISO2709) bibliographic format.
43 # ----------------------------------
46 use Net::Z3950::SimpleServer;
51 my $dbh = C4::Context->dbh;
52 my @bib_list; ## Stores the list of biblionumbers in a query
53 ## I should eventually move this to different scope
55 my $handler = Net::Z3950::SimpleServer->new(INIT => \&init_handler,
56 SEARCH => \&search_handler,
57 FETCH => \&fetch_handler);
59 $handler->launch_server("zed-koha-server.pl", @ARGV);
65 # FIXME: I should force use of my database name
66 $args->{IMP_NAME} = "Zed-Koha";
67 $args->{IMP_VER} = "0.02";
68 $args->{ERR_CODE} = 0;
69 $args->{HANDLE} = $session;
70 if (defined($args->{PASS}) && defined($args->{USER})) {
71 printf("Received USER/PASS=%s/%s\n", $args->{USER},$args->{PASS});
77 sub run_query { ## Run the query and store the biblionumbers:
78 my ($sql_query, $query, $args) = @_;
79 my $sth_get = $dbh->prepare("$sql_query");
81 ## Send the query to the database:
82 $sth_get->execute($query);
84 while(my ($data)=$sth_get->fetchrow_array) {
86 ## Store Biblioitem info for later
87 $bib_list[$count] = "$data";
92 $args->{HITS} = $count;
93 print "got search: ", $args->{RPN}->{query}->render(), "\n";
98 ## Place the user's query into a variable
99 my $query = $args->{QUERY};
102 my $term = $args->{term};
104 $term .= "\%"; ## Add the wildcard to search term
108 ## Strip out the junk and call the mysql query subroutine:
110 $query =~ s|\@attrset 1.2.840.10003.3.1 \@attr 1=7 ||g;
114 ## Bib-1 Structure Attributes:
115 $query =~ s|\@attr||g;
117 $query =~ s|4=1||g; ## Phrase
118 $query =~ s|4=2||g; ## Keyword
119 $query =~ s|4=3||g; ## Key
120 $query =~ s|4=4||g; ## year
121 $query =~ s|4=5||g; ## Date (normalized)
122 $query =~ s|4=6||g; ## word list
123 $query =~ s|4=100||g; ## date (un-normalized)
124 $query =~ s|4=101||g; ## name (normalized)
125 $query =~ s|4=102||g; ## sme (un-normalized)
127 $query =~ s|\@and ||g;
130 $query =~ s|,|%|g; ## replace commas with wildcard
131 $query .= "\%"; ## Add the wildcard to search term
132 $query .= "\%"; ## Add the wildcard to search term
133 print "The term was:\n";
135 print "The query was:\n";
137 my $sql_query = "SELECT biblionumber FROM biblioitems WHERE isbn LIKE ?";
138 &run_query($sql_query, $query, $args);
141 elsif (/1=1003/) { ## author
142 $query =~ s|\@attrset||g;
143 $query =~ s|1.2.840.10003.3.1||g;
144 $query =~ s|1=1003||g;
146 ## Bib-1 Structure Attributes:
147 $query =~ s|\@attr ||g;
149 $query =~ s|4=1||g; ## Phrase
150 $query =~ s|4=2||g; ## Keyword
151 $query =~ s|4=3||g; ## Key
152 $query =~ s|4=4||g; ## year
153 $query =~ s|4=5||g; ## Date (normalized)
154 $query =~ s|4=6||g; ## word list
155 $query =~ s|4=100||g; ## date (un-normalized)
156 $query =~ s|4=101||g; ## name (normalized)
157 $query =~ s|4=102||g; ## sme (un-normalized)
162 $query .= "\%"; ## Add the wildcard to search term
164 my $sql_query = "SELECT biblionumber FROM biblio WHERE author LIKE ?";
165 &run_query($sql_query, $query, $args);
166 ## used for debugging--works!
167 ## print "@bib_list\n";
169 elsif (/1=4/) { ## title
170 $query =~ s|\@attrset||g;
171 $query =~ s|1.2.840.10003.3.1||g;
176 ## Bib-1 Structure Attributes:
177 $query =~ s|\@attr||g;
179 $query =~ s|4=1||g; ## Phrase
180 $query =~ s|4=2||g; ## Keyword
181 $query =~ s|4=3||g; ## Key
182 $query =~ s|4=4||g; ## year
183 $query =~ s|4=5||g; ## Date (normalized)
184 $query =~ s|4=6||g; ## word list
185 $query =~ s|4=100||g; ## date (un-normalized)
186 $query =~ s|4=101||g; ## name (normalized)
187 $query =~ s|4=102||g; ## sme (un-normalized)
190 #$query =~ s|\@and||g;
191 $query .= "\%"; ## Add the wildcard to search term
192 print "The term was:\n";
194 print "The query was:\n";
196 my $sql_query = "SELECT biblionumber FROM biblio WHERE title LIKE ?";
197 &run_query($sql_query, $query, $args);
199 elsif (/1=21/) { ## subject
200 $query =~ s|\@attrset 1.2.840.10003.3.1 \@attr 1=21 ||g;
204 ## Bib-1 Structure Attributes:
205 $query =~ s|\@attr ||g;
207 $query =~ s|4=1||g; ## Phrase
208 $query =~ s|4=2||g; ## Keyword
209 $query =~ s|4=3||g; ## Key
210 $query =~ s|4=4||g; ## year
211 $query =~ s|4=5||g; ## Date (normalized)
212 $query =~ s|4=6||g; ## word list
213 $query =~ s|4=100||g; ## date (un-normalized)
214 $query =~ s|4=101||g; ## name (normalized)
215 $query =~ s|4=102||g; ## sme (un-normalized)
217 $query .= "\%"; ## Add the wildcard to search term
219 my $sql_query = "SELECT biblionumber FROM bibliosubject WHERE subject LIKE ?";
220 &run_query($sql_query, $query, $args);
222 elsif (/1=1016/) { ## any
223 $query =~ s|\@attrset 1.2.840.10003.3.1 \@attr 1=1016 ||g;
227 ## Bib-1 Structure Attributes:
228 $query =~ s|\@attr||g;
230 $query =~ s|4=1||g; ## Phrase
231 $query =~ s|4=2||g; ## Keyword
232 $query =~ s|4=3||g; ## Key
233 $query =~ s|4=4||g; ## year
234 $query =~ s|4=5||g; ## Date (normalized)
235 $query =~ s|4=6||g; ## word list
236 $query =~ s|4=100||g; ## date (un-normalized)
237 $query =~ s|4=101||g; ## name (normalized)
238 $query =~ s|4=102||g; ## sme (un-normalized)
240 $query .= "\%"; ## Add the wildcard to search term
242 my $sql_query = "SELECT bibid FROM marc_word WHERE word LIKE?";
243 &run_query($sql_query, $query, $args);
248 # warn "in fetch_handler"; ## troubleshooting
249 my $offset = $args->{OFFSET};
250 $offset -= 1; ## because $args->{OFFSET} 1 = record #1
251 chomp (my $bibid = $bib_list[$offset]); ## Not sure about this
252 ## print "the bibid is:$bibid\n";
253 my $MARCRecord = &MARCgetbiblio($dbh,$bibid);
254 my $recordstring=$MARCRecord->as_usmarc();
255 ## print "here is my record: $recordstring\n";
259 ## Dumper $recordstring;
260 ## open (MARC, ">/root/marc.dump");
261 ## print MARC "$recordstring";
264 ## Convert from 852/4 to 952:
265 ## 942a --> 852a Organization code
266 ## 952b --> 852b Home branch
267 ## 942k --> 852h Classification
268 ## 952p --> 852p Barcode
270 my $record = MARC::Record->new_from_usmarc($recordstring);
271 my @fields942 = $record->field('942');
272 my $field842 = $fields942[0];
273 my ($field952, $sub852a, $sub852k, $sub852b, $sub852p, $sub852h);
276 ## while ( my $record = $batch->next() ) {
277 ## my @fields942 = $record->field('942');
278 ## my $field842 = $fields942[0];
279 ## #grab first 942 (only need one, they are same for all items)
280 ## my $sub852a = ($field842->subfield('a') || '');
281 ## my $sub852h = ($field842->subfield('k') || '');
283 ## my @fields952 = $record->field('952');
284 ## foreach my $field952 (@fields952) { #get all 952s
285 ## my $sub852b = ($field952->subfield('b') || '');
286 ## my $sub852p = ($field952->subfield('p') || '');
289 #grab first 942 (only need one, they are same for all items)
290 unless (! $field952){
291 $sub852a = ($field952->subfield('a') || '') ;
293 unless (! $field952){ #->subfield('k')) {
294 $sub852k = ($field952->subfield('k') || '') ;
298 my @fields952 = $record->field('952');
299 foreach my $field952 (@fields952) { #get all 952s
301 unless (! $field952) { #->subfield('b')) {
302 $sub852b = ($field952->subfield('b') || '') ;
304 unless (! $field952) { #->subfield('p')) {
305 $sub852p = ($field952->subfield('p') || '') ;
307 #make it one big happy family
308 my $new852 = MARC::Field->new(
315 $record->append_fields($new852);
319 my $recordstringdone = $record->as_usmarc();
322 $args->{REP_FORM} = &Net::Z3950::OID::usmarc;
324 ## Return the record string to the client
325 $args->{RECORD} = $recordstringdone;
333 sub fetch_handler_old {
335 # warn "in fetch_handler"; ## troubleshooting
336 my $offset = $args->{OFFSET};
337 $offset -= 1; ## because $args->{OFFSET} 1 = record #1
338 chomp (my $bibid = $bib_list[$offset]); ## Not sure about this
339 my $sql_query = "SELECT tag, subfieldcode, subfieldvalue FROM marc_subfield_table where bibid=?";
340 my $sth_get = $dbh->prepare("$sql_query");
341 $sth_get->execute($bibid);
343 ## create a MARC::Record object
344 my $rec = MARC::Record->new();
347 while (my @data=$sth_get->fetchrow_array) {
350 my $subfieldcode = $data[1];
351 my $subfieldvalue = $data[2];
353 my $field = MARC::Field->new(
355 $subfieldcode => $subfieldvalue,
358 $rec->append_fields($field);
360 ## build the marc string and put into $record
361 my $tmp_record = $rec->as_usmarc();
362 my $reclen = length $tmp_record;
363 my $baseaddr = "$reclen + dirlen";
364 # set_leader_lengths($reclen,$baseaddr);
365 my $record = $rec->as_usmarc();
366 $args->{RECORD} = $record;
372 ## This stuff doesn't work yet...I should include boolean searching someday
374 package Net::Z3950::RPN::Term;
377 return '"' . $self->{term} . '"';
380 package Net::Z3950::RPN::And;
383 return '(' . $self->[0]->render() . ' AND ' .
384 $self->[1]->render() . ')';
387 package Net::Z3950::RPN::Or;
390 return '(' . $self->[0]->render() . ' OR ' .
391 $self->[1]->render() . ')';
394 package Net::Z3950::RPN::AndNot;
397 return '(' . $self->[0]->render() . ' ANDNOT ' .
398 $self->[1]->render() . ')';