#!/usr/bin/perl # send-it-to-me.cgi - given an isbn number, send the book to a person # Eric Lease Morgan # 2007-05-01 - first investigations # 2007-05-02 - added thingisbn # 2007-05-03 - added loc; added pseudo-send # 2007-05-11 - added find similar # 2009-12-03 - moved to zoia # 2010-01-24 - added GNU Public License # P.S. The entire application, complete with support files ought to be # available (at least temporarily) from: # # http://zoia.library.nd.edu/sandbox/send/send-2010-01-24.tar.gz # Copyright (C) Eric Lease Morgan # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2, # as published by the Free Software Foundation. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # define templates use constant TEMPLATE => './etc/template.txt'; use constant SUCCESS => './etc/success.txt'; use constant FAILURE => './etc/failure.txt'; use constant SEND => './etc/send.txt'; use constant NOTITLE => './etc/no-title.txt'; use constant SIMILAR => './etc/similar.txt'; # define web services use constant SEARCH => 'http://alephprod.library.nd.edu:8991/X?op=find&base=ndu01&code=020&request='; use constant AUSEARCH => 'http://alephprod.library.nd.edu:8991/X?op=find&base=ndu01&code=wau&request='; use constant SUSEARCH => 'http://alephprod.library.nd.edu:8991/X?op=find&base=ndu01&code=wsu&request='; use constant PRESENT => 'http://alephprod.library.nd.edu:8991/X?op=present&set_entry=000000001&base=ndu01&set_number='; use constant PRESENT10 => 'http://alephprod.library.nd.edu:8991/X?op=present&set_entry=000000001-000000010&base=ndu01&set_number='; use constant THINGISBN => 'http://www.librarything.com/api/thingISBN/'; use constant XISBN => 'http://old-xisbn.oclc.org/webservices/xisbn/'; use constant LOC => 'http://z3950.loc.gov:7090/voyager?operation=searchRetrieve&version=1.1&recordSchema=mods&maximumRecords=10&query=bath.isbn='; # define xslt stylesheets use constant SEARCH2TAB => './etc/search2tab.xsl'; use constant PRESENT2TAB => './etc/present2tab.xsl'; use constant PRESENT102TAB => './etc/present102tab.xsl'; use constant XISBN2TAB => './etc/xisbn2tab.xsl'; use constant LOC2AUTHORTITLE => './etc/loc2authortitle.xsl'; use constant LOC2AUTHOR => './etc/loc2author.xsl'; use constant LOC2TITLE => './etc/loc2title.xsl'; use constant LOC2SUBJECT => './etc/loc2subjects.xsl'; # define instance use constant INSTANCE => 'send_it_to_me'; # require/include use CGI; use CGI::Carp qw( fatalsToBrowser ); use LWP::UserAgent; use strict; use XML::LibXML; use XML::LibXSLT; use HTML::Entities; use MyLibrary::Core; use MyLibrary::Patron; # initialize my $cgi = CGI->new; my $html = ''; MyLibrary::Config->instance( INSTANCE ); # get the command my $cmd = $cgi->param( 'cmd' ); if ( ! $cmd ) { # create the home page my $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##//e; $html =~ s/##ISBN##//e; # done &gracefulExit ( $html ); } elsif ( $cmd eq 'similar' ) { my $isbn = $cgi->param( 'isbn' ); # get a particular record my $url = LOC . $isbn; my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; # get the first author my $style = $parser->parse_file( LOC2AUTHOR ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; my $author = $stylesheet->output_string( $results ); $author =~ s/,//g; $author =~ s/\.//g; $author =~ s/ /%20!0%20/g; my $author_search = AUSEARCH . $author; my ( $number_of_aurecords, $auset_number ) = &catalog_search( $author_search ); my $author_list; if ( ! $number_of_aurecords ) { $author_list = $cgi->li( '(No records found.)' )} else { foreach ( &get_titles( $auset_number )) { my ( $title, $author, $isbn ) = split /\t/; $author_list .= $cgi->li( $title . ' / ' . $author . ' (' . $cgi->a({ href => "javascript:Q=prompt('What%20is%20your%20university%20ID?%20(ex:%20frankwright)?','');location.href='http://zoia.library.nd.edu/sandbox/send/?cmd=send&isbn=$isbn&netid='+Q" }, 'Send it to me' ) . '.)' ); } } $author_list = $cgi->ul( $author_list ); # get the subjects $style = $parser->parse_file( LOC2SUBJECT ) or croak $!; $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; $results = $stylesheet->transform( $source ) or croak $!; my $subjects = $stylesheet->output_string( $results ); my ( $number_of_surecords, $suset_number ); my $subject_list; foreach ( split /\t\n/, $subjects ) { s/\t/ /g; s/\W/ /g; while ( / / ) { s/ / / } s/ /%20/g; my $subject_search = SUSEARCH . $_; ( $number_of_surecords, $suset_number ) = &catalog_search( $subject_search ); foreach ( &get_titles( $suset_number )) { my ( $title, $author, $isbn ) = split /\t/; $subject_list .= $cgi->li( $title . ' / ' . $author . ' (' . $cgi->a({ href => "javascript:Q=prompt('What%20is%20your%20university%20ID?%20(ex:%20frankwright)?','');location.href='http://zoia.library.nd.edu/sandbox/send/?cmd=send&isbn=$isbn&netid='+Q" }, 'Send it to me' ) . '.)' ); } } $subject_list = $cgi->ul( $subject_list ); # create the results page my $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( SIMILAR )/e; $html =~ s/##ISBN##/$isbn/e; $html =~ s/##AUTHOR##/$author_list/e; $html =~ s/##SUBJECTS##/$subject_list/e; # done &gracefulExit ( $html ); } elsif ( $cmd eq 'send' ) { # initialize my $isbn = $cgi->param( 'isbn' ); my $netid = $cgi->param( 'netid' ); # get patron information my $patron = MyLibrary::Patron->new( username => $netid ); if (! $patron ) { &error( "The NetID ($netid) was not found. Call Eric." )} my $first_name = $patron->patron_firstname; my $last_name = $patron->patron_surname; my $email = $patron->patron_email; # create the results page my $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( SEND )/e; $html =~ s/##ISBN##/$isbn/e; $html =~ s/##FIRST##/$first_name/e; $html =~ s/##LAST##/$last_name/e; $html =~ s/##EMAIL##/$email/e; $html =~ s/##NUMBER##/int(rand( 1000000 ))/e; # done &gracefulExit ( $html ); } elsif ( $cmd eq 'search' ) { # initilize my $isbn = $cgi->param( 'isbn' ); my $found = 'not found'; my ( $title, $author, $new_isbn ); # search the local catalog my ( $number_of_records, $set_number ) = &isbn_search( $isbn ); if ( $number_of_records ) { $found = 'found'; ( $title, $author) = &get_title( $set_number ); } # search xisbn numbers if ( $found eq 'not found' ) { # query xisbn for other numbers my @xisbns = &xisbn( $isbn ); foreach my $xisbn ( @xisbns ) { my ( $number_of_records, $set_number ) = &isbn_search( $xisbn ); if ( $number_of_records ) { $found = 'found'; ( $title, $author) = &get_title( $set_number ); last; } } } # search thingisbn numbers if ( $found eq 'not found' ) { # query thingisbn for other numbers my @thingisbns = &thingisbn( $isbn ); foreach my $thingisbn ( @thingisbns ) { my ( $number_of_records, $set_number ) = &isbn_search( $thingisbn ); if ( $number_of_records ) { $found = 'found'; ( $title, $author) = &get_title( $set_number ); last; } } } # sanity check against loc if ( $found eq 'not found' ) { # use the given input to search loc ( $title, $author ) = &loc( $isbn ); $new_isbn = $isbn; # use xisbn to search loc if ( ! $title ) { # query xisbn for other numbers my @xisbns = &xisbn( $isbn ); foreach my $xisbn ( @xisbns ) { ( $title, $author ) = &loc( $xisbn ); $new_isbn = $xisbn; last if ( $title); } } # use thingisbn to search loc if ( ! $title ) { # query thingisbn for other numbers my @thingisbns = &thingisbn( $isbn ); foreach my $thingisbn ( @thingisbns ) { ( $title, $author ) = &loc( $thingisbn ); $new_isbn = $thingisbn; last if ( $title); } } } # build the results page my $template; my $html; if ( ! $title ) { $template = NOTITLE; $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( $template )/e; $html =~ s/##ISBN##/$isbn/ge; } elsif ( $found eq 'not found' ) { $template = FAILURE; $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( $template )/e; $html =~ s/##ISBN##/$isbn/ge; $html =~ s/##TITLE##/$title/e; $html =~ s/##AUTHOR##/$author/e; $html =~ s/##NEWISBN##/$new_isbn/ge; } else { $template = SUCCESS; $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( $template )/e; $html =~ s/##ISBN##/$isbn/ge; $html =~ s/##FOUND##/$found/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##AUTHOR##/$author/e; } # done &gracefulExit ( $html ); } else { # unknown command &error( "Error: unknown value for cmd ($cmd). Call Eric." ); } ############# # subroutines sub slurp { # open a file named by the input and return its contents my $f = @_[0]; my $r; open (F, "< $f"); while () { $r .= $_ } close F; return $r; } # build an error screen sub error { my $msg = shift; $msg = $cgi->p( $msg ); my $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/$msg/; &gracefulExit ( $html ); } # quit the program sub gracefulExit { my $html = shift; print $cgi->header(-type=>'text/html', -charset => 'UTF-8'); print $html; exit; } sub get_title { my $set_number = shift; # build the present request and get it my $url = PRESENT . $set_number; my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( PRESENT2TAB ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; # clean my ($title, $author ) = split /\t/, $stylesheet->output_string( $results ); $title =~ s/ \/$//; $title =~ s/ :$//; $title =~ s/\.$//; $author =~ s/\.$//; # done return $title, $author; } sub get_titles { my $set_number = shift; # build the present request and get it my $url = PRESENT10 . $set_number; my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( PRESENT102TAB ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; # clean my @items = split /\n/, $stylesheet->output_string( $results ); my @return; foreach ( @items ) { my ( $title, $author, $isbn ) = split /\t/; $title =~ s/ \/$//; $title =~ s/ :$//; $title =~ s/\.$//; $author =~ s/\.$//; my @isbn = split / /, $isbn; $isbn = $isbn[0]; push @return, "$title\t$author\t$isbn"; } # done return @return; } sub isbn_search { my $isbn = shift; # create the x-server request my $url = SEARCH . $isbn; # create a user agent, create a request, send it, and get a response my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); # transform the response my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( SEARCH2TAB ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; my ( $number_of_records, $set_number) = split /\t/, $stylesheet->output_string( $results ); while ( $number_of_records =~ /^0/ ) { $number_of_records =~ s/^0// } return ( $number_of_records, $set_number ); } sub catalog_search { my $url = shift; # create a user agent, create a request, send it, and get a response my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); # transform the response my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( SEARCH2TAB ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; my ( $number_of_records, $set_number) = split /\t/, $stylesheet->output_string( $results ); while ( $number_of_records =~ /^0/ ) { $number_of_records =~ s/^0// } return ( $number_of_records, $set_number ); } sub xisbn { my $isbn = shift; # get a list of similar isbn numbers from xisbn my $url = XISBN . $isbn; my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( XISBN2TAB ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; return split /\t/, $stylesheet->output_string( $results ); } sub thingisbn { my $isbn = shift; # get a list of similar isbn numbers from thingisbn my $url = THINGISBN . $isbn; my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( XISBN2TAB ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; return split /\t/, $stylesheet->output_string( $results ); } sub loc { my $isbn = shift; # find this isbn number in loc my $url = LOC . $isbn; my $ua = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( LOC2AUTHORTITLE ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; # return "$author\t$title" return split /\t/, $stylesheet->output_string( $results ); }