From d20b75c5365cd390e415faa0e35af339c6aac1be Mon Sep 17 00:00:00 2001 From: Mason James <mtj@kohaaloha.com> Date: Thu, 28 Apr 2016 23:09:45 +1200 Subject: [PATCH] Bug 10756 - Carousel Display of New Titles on OPAC home page test plan... 1/ import some bibs into your database for testing (important) $ wget http://www.loc.gov/catdir/cpso/RDAtest/extra_bib_marc.zip $ unzip extra_bib_marc.zip $ bulkmarcimport.pl -b -m ISO2709 -file ./formal_extra_all_final.bib 2/ apply patches 3/ execute atomic update to add syspref and 'Carousel' table $ perl ./installer/data/mysql/atomicupdate/Bug-10756-carousel.pl 4/ open OPAC homepage in web-browser, observe no change 5/ set 'OpacCarousel' syspref to 'display' 6/ open OPAC homepage in web-browser, observe a new carousel :0) extra QA tests... 7/ run 5 carousel tests $ prove ./t/db_dependent/Carousel.t 8/ run qa-tool on patchset $ qa -c 2 Signed-off-by: Magnus Enger <magnus@libriotech.no> Followed the test plan, everything worked as expected. The front page displays a nice coverflow widget. More parameters to control the widget would be nice, but that could be added later. On first running this I got a nasty 500 error: "Can't call method "is_valid" on an undefined value at /home/magnus/scripts/kohaclone/Koha/Carousel.pm line 128." But I think I found a cure for that and will propose a followup patch. --- Koha/Carousel.pm | 278 +++++++++++++++++++++ Koha/Schema/Result/Carousel.pm | 79 ++++++ debian/koha-common.cron.daily | 2 +- .../data/mysql/atomicupdate/Bug-10756-carousel.pl | 20 ++ .../prog/en/modules/admin/preferences/opac.pref | 7 + .../bootstrap/en/includes/opac-bottom.inc | 16 ++ .../opac-tmpl/bootstrap/en/modules/opac-main.tt | 18 ++ misc/cronjobs/cleanup_database.pl | 27 +- opac/opac-main.pl | 9 + t/db_dependent/Carousel.t | 243 ++++++++++++++++++ 10 files changed, 696 insertions(+), 3 deletions(-) create mode 100755 Koha/Carousel.pm create mode 100644 Koha/Schema/Result/Carousel.pm create mode 100755 installer/data/mysql/atomicupdate/Bug-10756-carousel.pl create mode 100755 t/db_dependent/Carousel.t diff --git a/Koha/Carousel.pm b/Koha/Carousel.pm new file mode 100755 index 0000000..ec6a7f7 --- /dev/null +++ b/Koha/Carousel.pm @@ -0,0 +1,278 @@ +package Koha::Carousel; + +# Copyright 2016 Mason James +# +# 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 strict; +use warnings; +use C4::Koha; +use C4::Biblio; +use C4::Debug; + +use LWP::Simple; +use List::Util qw(shuffle ); +use Business::ISBN; + +if ( $ENV{DEBUG} ) { + use Time::HiRes qw/gettimeofday tv_interval/; + use Carp; +} + +use vars qw($VERSION @ISA @EXPORT); + +BEGIN { + $VERSION = 1.00; + + require Exporter; + @ISA = qw( Exporter ); + + push @EXPORT, qw( + &GetRecentBibs + ); +} + +=head1 NAME + +Koha::Carousel - A module to handle subroutines for the 'new items' carousel + +=head1 DESCRIPTION + +This module has 1 subroutine, + + GetRecentBibs() returns a hashref of bibs with a matching amazon bookcover url + +=head1 SYNOPSIS + + use Koha::Carousel + + # give me a hashref of 10 random new bibs + my $new_bibs_href = GetRecentBibs(); + + # give me a hashref of 20 random new bibs + my $new_bibs_href = GetRecentBibs('20'); + +=cut + +sub GetRecentBibs { + + my $caro_num = shift; + + # speed stats for debugging. + my $tt0; + my $tt1; + if ( $ENV{DEBUG} ) { + $tt0 = [gettimeofday]; + } + + # set vars = 0, to shush warnings + my ( $amz_hit_cnt, $amz_miss_cnt, $dupes, $cache_hits, $fetches, $hits, + $cache_misses ) + = (0) x 7; + my ( @bibs_stage1, @bibs_stage2, @bibs_stage3 ); + + my $ua = new LWP::UserAgent( 'max_redirect' => 0 ); + + # 3 different limits, for narrowing the list of bib results + my $stage1_limit = 300; + my $stage2_limit = 150; + my $stage3_limit = $caro_num ? $caro_num : 10; + +# Initial SQL query to get recently added bibs +# we choose a 'reasonable' limit here, to ensure we don't make our dataset too small + + my $bib_rs = Koha::Database->new()->schema->resultset('Biblio')->search( + { 'biblioitems.isbn' => { '!=', undef }, }, + { + join => 'biblioitems', + '+select' => ['biblioitems.isbn'], + '+as' => ['isbn'], + + rows => $stage1_limit, + desc => 'datecreated', + } + ); + + # run a loop to get $stage1_limit bibs with checksum verified ISBNs. + my $i; + while ( my $r1 = $bib_rs->next ) { + + # test checksum of ISBN10 + my $isbn_tmp = $r1->get_column('isbn'); + my $isbn; + + my @ibs = split( /\|/, $isbn_tmp ); + foreach my $ii (@ibs) { + + # strip trailing (comments) from isbn + $ii =~ s/\(.*$//g; + + my $isbn_obj = Business::ISBN->new($ii); + next unless $isbn_obj; + + my $isbn10; + $isbn10 = $isbn_obj->as_isbn10; + if ( $isbn10->is_valid() ) { + $isbn = $isbn10->as_string( [] ); + last; + } + } + + next unless $isbn; + + my $bib = { + biblionumber => $r1->biblionumber, + title => $r1->title, + author => $r1->author, + isbn => $isbn, + }; + + push @bibs_stage1, $bib; + + } # end while + + # run a loop to remove any dupes + $i = 0; + foreach my $bib (@bibs_stage1) { + + # check for dups before adding + my $dupe = 0; + foreach my $b (@bibs_stage2) { + if ( $bib->{isbn} eq $b->{isbn} ) { + $dupe = 1; + $dupes++; + last; + } + } + next if $dupe == 1; + push @bibs_stage2, $bib; + last if ++$i == $stage2_limit; + } # end while + + #randomise our bibs + @bibs_stage2 = shuffle @bibs_stage2; + + # loop, until we get enough cover images + # add each amazon lookup attempt to the carousel db table, for future caching + + for my $bib (@bibs_stage2) { + + # check for an existing ISBN row, in caro table + my $caro_rs = + Koha::Database->new()->schema->resultset('Carousel') + ->search( { 'isbn' => { '=', $bib->{'isbn'} }, }, { rows => '1' } ); + + my $cnt = $caro_rs->count; + + my $rs = $caro_rs->next; + my $amz_miss; + my $image_url; + + # if not db match, do amazon lookup.... + if ( $cnt == 0 ) { + + # no row in table, so fetch from amazon + $image_url = + 'http://images.amazon.com/images/P/' + . $bib->{isbn} + . '.01._THUMBZZZ.jpg'; + + my $req = HTTP::Request->new( 'GET', $image_url ); + my $res = $ua->request($req); + my $res_size = $res->headers->{'content-length'}; + + $fetches++; + + # warn $res_size; + # 43 bytes returned, means lookup was a miss :'( + $amz_miss = 1 if ( $res_size == 43 ); + + # insert lookup result into table + $caro_rs->create( + { + isbn => $bib->{'isbn'}, + image_url => ( $amz_miss ? undef : $image_url ) + } + ); + + if ($amz_miss) { + $amz_miss_cnt++; + next; + } + else { + # else, got a match from amazon + $amz_hit_cnt++; + } + + } # end + # else, got result from db.. + else { + + # but no image, so skip :/ + unless ( $rs->get_column('image_url') ) { + $cache_misses++; + next; + } + else { + # db result has image, so use ;) + $image_url = $rs->get_column('image_url'); + $cache_hits++; + } + } # end for + + # remove glitchy trailing chars from title/author for display /:, + foreach ( $bib->{title}, $bib->{author} ) { + next unless $_; # shush warns + s|/$||; + s|:$||; + s|,$||; + s/[ \t]+$//g; + } + + $bib->{image_url} = $image_url; + $bib->{id} = $hits; + + push @bibs_stage3, $bib; + + $hits++; + last if $hits == $stage3_limit; + } + + # if debug, print some extra stats + if ( $ENV{DEBUG} ) { + $tt1 = [gettimeofday]; + carp 'carousel: load time ' . tv_interval( $tt0, $tt1 ); + carp "carousel: db_hits: $cache_hits, amz_fetches:$fetches"; + carp +"amz_hit:$amz_hit_cnt, amz_miss:$amz_miss_cnt, dupes:$dupes, hits: $hits, misses: $cache_misses, needs: $stage3_limit"; + } + + return \@bibs_stage3; +} + +=head1 EXPORT + +None by default. + +=head1 AUTHOR + +Mason James, E<lt>mason@calyx.net.auE<gt> + +=cut + +1; + +__END__ diff --git a/Koha/Schema/Result/Carousel.pm b/Koha/Schema/Result/Carousel.pm new file mode 100644 index 0000000..3cd2581 --- /dev/null +++ b/Koha/Schema/Result/Carousel.pm @@ -0,0 +1,79 @@ +use utf8; +package Koha::Schema::Result::Carousel; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +=head1 NAME + +Koha::Schema::Result::Carousel + +=cut + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +=head1 TABLE: C<carousel> + +=cut + +__PACKAGE__->table("carousel"); + +=head1 ACCESSORS + +=head2 isbn + + data_type: 'varchar' + is_nullable: 0 + size: 30 + +=head2 image_url + + data_type: 'varchar' + is_nullable: 1 + size: 255 + +=head2 timestamp + + data_type: 'timestamp' + datetime_undef_if_invalid: 1 + default_value: current_timestamp + is_nullable: 0 + +=cut + +__PACKAGE__->add_columns( + "isbn", + { data_type => "varchar", is_nullable => 0, size => 255 }, + "image_url", + { data_type => "varchar", is_nullable => 0, size => 255 }, + "timestamp", + { + data_type => "timestamp", + datetime_undef_if_invalid => 1, + default_value => \"current_timestamp", + is_nullable => 0, + }, +); + +=head1 PRIMARY KEY + +=over 4 + +=item * L</isbn> + +=back + +=cut + +__PACKAGE__->set_primary_key("isbn"); + + +# Created by DBIx::Class::Schema::Loader v0.07025 @ 2014-11-15 02:47:15 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:WlpGsPHQes9PIDaN4/6zaA + + +# You can replace this text with custom code or comments, and it will be preserved on regeneration +1; diff --git a/debian/koha-common.cron.daily b/debian/koha-common.cron.daily index cbd7eed..9830840 100644 --- a/debian/koha-common.cron.daily +++ b/debian/koha-common.cron.daily @@ -21,7 +21,7 @@ koha-foreach --enabled --email /usr/share/koha/bin/cronjobs/advance_notices.pl - koha-foreach --enabled /usr/share/koha/bin/cronjobs/membership_expiry.pl -c koha-foreach --enabled /usr/share/koha/bin/cronjobs/holds/cancel_expired_holds.pl >/dev/null 2>&1 koha-foreach --enabled /usr/share/koha/bin/cronjobs/services_throttle.pl > /dev/null 2>&1 -koha-foreach --enabled /usr/share/koha/bin/cronjobs/cleanup_database.pl --sessions --zebraqueue 10 --list-invites +koha-foreach --enabled /usr/share/koha/bin/cronjobs/cleanup_database.pl --sessions --zebraqueue 10 --list-invites --carousel koha-foreach --enabled --noemail /usr/share/koha/bin/cronjobs/cleanup_database.pl --mail koha-foreach --enabled /usr/share/koha/bin/cronjobs/holds/auto_unsuspend_holds.pl > /dev/null 2>&1 koha-foreach --enabled /usr/share/koha/bin/cronjobs/automatic_renewals.pl diff --git a/installer/data/mysql/atomicupdate/Bug-10756-carousel.pl b/installer/data/mysql/atomicupdate/Bug-10756-carousel.pl new file mode 100755 index 0000000..724f9f0 --- /dev/null +++ b/installer/data/mysql/atomicupdate/Bug-10756-carousel.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use C4::Context; +my $dbh = C4::Context->dbh; + +$dbh->do( +q| +CREATE TABLE `carousel` ( + `isbn` varchar(255) NOT NULL, + `image_url` varchar(255) NULL, + `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`isbn`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +); + + $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('OpacCarousel','0','Display the new-items carousel on OPAC home page.','','YesNo')"); + +print "Upgrade done (Bug 10756 - Carousel Display of New Titles on OPAC)\n"; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref index f092767..9f85f19 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref @@ -490,6 +490,13 @@ OPAC: no: Don't display - the acquisition details on OPAC detail pages. - + - pref: OPACCarousel + default: 0 + choices: + yes: Display + no: Don't display + - the 'new items' carousel on OPAC home page. + - - "Use the following as the OPAC ISBD template:" - pref: OPACISBD type: textarea diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc index 9e7a15c..7c14881 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc @@ -189,6 +189,22 @@ $.widget.bridge('uitooltip', $.ui.tooltip); </script> [% END %] +[% IF OpacCarousel %] + <script type="text/javascript" src="[% interface %]/[% theme %]/lib/contentflow/contentflow.js"></script> + <script type="text/javascript"> + //<![CDATA[ + $(document).ready(function() { + var cflow2 = new ContentFlow('cflow2', { + reflectionHeight : 0.2 , + reflectionGap : 0.0, + scaleFactor : 2.0, + }); + cflow2.init() + }); + //]]> + </script> +[% END %] + [% IF OPACLocalCoverImages %] <script type="text/javascript" src="[% interface %]/[% theme %]/js/localcovers.js"></script> <script type="text/javascript"> diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-main.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-main.tt index 798c90e..387207d 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-main.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-main.tt @@ -66,6 +66,24 @@ [% END %] [% IF ( OpacMainUserBlock ) %]<div id="opacmainuserblock">[% OpacMainUserBlock %]</div>[% END %] + + [% IF ( caro_bibs ) %] + <div id="cflow1" class="span6 offset3"> + <center><h3>Recently added items</h3></center> + <div id="cflow2" class="ContentFlow" style="background: hidden;"> + <div class="flow"> + [% FOREACH caro_bibs IN caro_bibs %] + <div class="item" href="/cgi-bin/koha/opac-detail.pl?bib=[% caro_bibs.biblionumber %]" > + <img class="content" src="[% caro_bibs.image_url %]" /> + <div class="caption">[% caro_bibs.title %][% IF ( caro_bibs.author ) %] by [% caro_bibs.author %][% END %]</div> + </div> + [% END %] + </div> + <div class="globalCaption"></div> + </div> + </div> + [% END %] + </div> <!-- / .span 7/9 --> [% IF ( ( Koha.Preference( 'opacuserlogin' ) == 1 ) || OpacNavRight ) %] diff --git a/misc/cronjobs/cleanup_database.pl b/misc/cronjobs/cleanup_database.pl index bb54256..f5013c6 100755 --- a/misc/cronjobs/cleanup_database.pl +++ b/misc/cronjobs/cleanup_database.pl @@ -23,6 +23,7 @@ use constant DEFAULT_ZEBRAQ_PURGEDAYS => 30; use constant DEFAULT_MAIL_PURGEDAYS => 30; use constant DEFAULT_IMPORT_PURGEDAYS => 60; use constant DEFAULT_LOGS_PURGEDAYS => 180; +use constant DEFAULT_CAROUSEL_PURGEDAYS => 90; use constant DEFAULT_SEARCHHISTORY_PURGEDAYS => 30; use constant DEFAULT_SHARE_INVITATION_EXPIRY_DAYS => 14; use constant DEFAULT_DEBARMENTS_PURGEDAYS => 30; @@ -43,7 +44,7 @@ use C4::Accounts; sub usage { print STDERR <<USAGE; -Usage: $0 [-h|--help] [--sessions] [--sessdays DAYS] [-v|--verbose] [--zebraqueue DAYS] [-m|--mail] [--merged] [--import DAYS] [--logs DAYS] [--searchhistory DAYS] [--restrictions DAYS] [--all-restrictions] [--fees DAYS] +Usage: $0 [-h|--help] [--sessions] [--sessdays DAYS] [-v|--verbose] [--zebraqueue DAYS] [-m|--mail] [--merged] [--import DAYS] [--logs DAYS] [--searchhistory DAYS] [--restrictions DAYS] [--all-restrictions] [--fees DAYS] [--carousel DAYS] -h --help prints this help message, and exits, ignoring all other options @@ -76,9 +77,11 @@ Usage: $0 [-h|--help] [--sessions] [--sessdays DAYS] [-v|--verbose] [--zebraqueu days. Defaults to 14 days if no days specified. --restrictions DAYS purge patrons restrictions expired since more than DAYS days. Defaults to 30 days if no days specified. - --all-restrictions purge all expired patrons restrictions. + --all-restrictions purge all expired patrons restrictions. --del-exp-selfreg Delete expired self registration accounts --del-unv-selfreg DAYS Delete unverified self registrations older than DAYS + --carousel DAYS purge entries from carousel older than DAYS days. + Defaults to 90 days if no days specified. USAGE exit $_[0]; } @@ -92,6 +95,7 @@ my $mail; my $purge_merged; my $pImport; my $pLogs; +my $pCarousel; my $pSearchhistory; my $pZ3950; my $pListShareInvites; @@ -113,6 +117,7 @@ GetOptions( 'z3950' => \$pZ3950, 'logs:i' => \$pLogs, 'fees:i' => \$fees_days, + 'carousel:i' => \$pCarousel, 'searchhistory:i' => \$pSearchhistory, 'list-invites:i' => \$pListShareInvites, 'restrictions:i' => \$pDebarments, @@ -125,6 +130,7 @@ GetOptions( $sessions = 1 if $sess_days && $sess_days > 0; $pImport = DEFAULT_IMPORT_PURGEDAYS if defined($pImport) && $pImport == 0; $pLogs = DEFAULT_LOGS_PURGEDAYS if defined($pLogs) && $pLogs == 0; +$pCarousel = DEFAULT_CAROUSEL_PURGEDAYS if defined($pCarousel) && $pCarousel == 0; $zebraqueue_days = DEFAULT_ZEBRAQ_PURGEDAYS if defined($zebraqueue_days) && $zebraqueue_days == 0; $mail = DEFAULT_MAIL_PURGEDAYS if defined($mail) && $mail == 0; $pSearchhistory = DEFAULT_SEARCHHISTORY_PURGEDAYS if defined($pSearchhistory) && $pSearchhistory == 0; @@ -142,6 +148,7 @@ unless ( $sessions || $pImport || $pLogs || $fees_days + || $pCarousel || $pSearchhistory || $pZ3950 || $pListShareInvites @@ -238,6 +245,12 @@ if ($pZ3950) { print "Done with purging Z39.50 records from import tables.\n" if $verbose; } +if ($pCarousel) { + print "Purging records from carousel tables.\n" if $verbose; + PurgeCarousel(); + print "Done with purging records from carousel tables.\n" if $verbose; +} + if ($pLogs) { print "Purging records from action_logs.\n" if $verbose; $sth = $dbh->prepare( @@ -363,6 +376,16 @@ sub PurgeZ3950 { $sth->execute() or die $dbh->errstr; } +sub PurgeCarousel { + $sth = $dbh->prepare( + q{ + DELETE FROM carousel + WHERE timestamp < date_sub(curdate(), INTERVAL ? DAY) + } + ); + $sth->execute($pCarousel) or die $dbh->errstr; +} + sub PurgeDebarments { require Koha::Patron::Debarments; my $days = shift; diff --git a/opac/opac-main.pl b/opac/opac-main.pl index c79afb4..aeb8c61 100755 --- a/opac/opac-main.pl +++ b/opac/opac-main.pl @@ -25,6 +25,7 @@ use C4::Output; use C4::NewsChannels; # GetNewsToDisplay use C4::Languages qw(getTranslatedLanguages accept_language); use C4::Koha qw( GetDailyQuote ); +use Koha::Carousel; my $input = new CGI; my $dbh = C4::Context->dbh; @@ -72,4 +73,12 @@ if (C4::Context->preference('OPACNumbersPreferPhrase')) { $template->param('numbersphr' => 1); } +if ( C4::Context->preference('OpacCarousel') ) { + our $caro_bibs = GetRecentBibs(10); + $template->param( + caro_bibs => $caro_bibs, + OpacCarousel => 1 + ); +} + output_html_with_http_headers $input, $cookie, $template->output; diff --git a/t/db_dependent/Carousel.t b/t/db_dependent/Carousel.t new file mode 100755 index 0000000..d1f3761 --- /dev/null +++ b/t/db_dependent/Carousel.t @@ -0,0 +1,243 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Copyright 2016 CALYX Group +# +# 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 Test::MockModule; + +use MARC::Record; +use t::lib::Mocks qw( mock_preference ); + +BEGIN { + use_ok('C4::Biblio'); + use_ok('Koha::Carousel'); +} + +my $dbh = C4::Context->dbh; + +$dbh->{AutoCommit} = 0; +$dbh->{RaiseError} = 1; + +# Mock a context +my $context = new Test::MockModule('C4::Context'); +mock_marcfromkohafield(); + +# create 3 sets of ISBN run to tests on... +# 1 set of invalid isbns +my @isbns_bad = qw | + 1111 + 2222 + 333 + 444 + 5555 + 666 + 777 + 88 + 99 + 1100000 + + |; + +# 1 set of mixed valid and invalid isbns +my @isbns_mixed = qw | + 2846542082 + 2848301384 + 2848672870 + 2953079971 + 2953636501 + 11111 + 2222 + 33333 + 4444444 + 55555 + + |; + +# 1 set of valid isbns +my @isbns_good = qw | + 2846542082 + 2848301384 + 2848672870 + 2953079971 + 2953636501 + 3110251345 + 3406606067 + 3531167707 + 3531174142 + 3531174827 + + |; + +my $marcflavour; + +# run some tests for MARC21, UNIMARC and NORMARC bibsets +sub run_tests { + + $marcflavour = shift; + + # clean the bib tables for the before starting the tests, with auto-commit off + my $sth; + $sth = $dbh->do("delete from items"); + $sth = $dbh->do("delete from biblioitems"); + + $sth = $dbh->do("delete from biblio"); + $sth = $dbh->do("delete from caro_isbn_url"); + + # Undef C4::Biblio::inverted_field_map to avoid problems introduced + # by caching in TransformMarcToKoha + undef $C4::Biblio::inverted_field_map; + t::lib::Mocks::mock_preference( 'marcflavour', $marcflavour ); + + # add some bibs with bad isbns + our $caro_bibs_bad = GetRecentBibs(10); + + # test for 10 invalid ISBNs, expects 0 successful results + is( + scalar @$caro_bibs_bad, + 0, + 'Number of invalid bibs returned is ' + . scalar @$caro_bibs_bad + . ', expected 0' + ); + + + foreach my $i (@isbns_mixed) { create_bib($i); } + our $caro_bibs_mixed = GetRecentBibs(10); + + # test for 10 mixed ISBNs, expects 5 successful results + is( + scalar @$caro_bibs_mixed, + 5, + 'Number of mixed bibs returned is ' + . scalar @$caro_bibs_mixed + . ', expected 5' + ); + + # test for 10 valid ISBNs, expects 10 successful results + foreach my $i (@isbns_good) { create_bib($i); } + our $caro_bibs_good = GetRecentBibs(10); + + # then... add some more bibs with good isbns + is( + scalar @$caro_bibs_good, + 10, + 'Number of valid bibs returned is ' + . scalar @$caro_bibs_good + . ', expected 10' + ); +} + +# mock a needed MARC subfield, for testing +sub mock_marcfromkohafield { + + $context->mock( + 'marcfromkohafield', + sub { + my ($self) = shift; + + # if we are testing MARC21 or NORMARC, use these subfields... + if ( C4::Context->preference('marcflavour') eq 'MARC21' + || C4::Context->preference('marcflavour') eq 'NORMARC' ) + { + return { + '' => { + 'biblio.title' => [ '245', 'a' ], + 'biblio.biblionumber' => [ '999', 'c' ], + 'biblioitems.isbn' => [ '020', 'a' ], + 'biblioitems.issn' => [ '022', 'a' ], + 'biblioitems.biblioitemnumber' => [ '999', 'd' ] + } + }; + } + + # or, if we are testing UNIMARC, use these subfields... + elsif ( C4::Context->preference('marcflavour') eq 'UNIMARC' ) { + + return { + '' => { + 'biblio.title' => [ '200', 'a' ], + 'biblio.biblionumber' => [ '999', 'c' ], + 'biblioitems.isbn' => [ '010', 'a' ], + 'biblioitems.issn' => [ '011', 'a' ], + 'biblioitems.biblioitemnumber' => [ '090', 'a' ] + } + }; + } + } + ); +} + +# create a simple mocked bib +sub create_bib { + + my $isbn = shift; + my $title = 'some title'; + + # Generate a record with just the ISBN + my $marc_record = MARC::Record->new; + my $isbn_field = create_isbn_field( $isbn, $marcflavour ); + $marc_record->append_fields($isbn_field); + + # Add title + my $fld = ( $marcflavour eq 'UNIMARC' ) ? '200' : '245'; + my $title_field = MARC::Field->new( $fld, '', '', 'a' => $title ); + $marc_record->append_fields($title_field); + + # Add the record to the DB + my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' ); + my $data = GetBiblioData($biblionumber); + +} + +# Add an isbn subfield to an item +sub create_isbn_field { + my ( $isbn, $marcflavour ) = @_; + + my $isbn_field = ( $marcflavour eq 'UNIMARC' ) ? '010' : '020'; + my $field = MARC::Field->new( $isbn_field, '', '', 'a' => $isbn ); + + return $field; +} + +# do tests on 3 different MARC formats +subtest 'MARC21' => sub { + plan tests => 3; + run_tests('MARC21'); + + $dbh->rollback; +}; + +subtest 'UNIMARC' => sub { + plan tests => 3; + run_tests('UNIMARC'); + + $dbh->rollback; +}; + +subtest 'NORMARC' => sub { + plan tests => 3; + run_tests('NORMARC'); + + $dbh->rollback; +}; + +done_testing(); + +1; -- 1.9.1