Jonathan Druart
9d6d641d1f
On bug 17591 we discovered that there was something weird going on with the way we export and use subroutines/modules. This patch tries to standardize our EXPORT to use EXPORT_OK only. That way we will need to explicitely define the subroutine we want to use from a module. This patch is a squashed version of: Bug 17600: After export.pl Bug 17600: After perlimport Bug 17600: Manual changes Bug 17600: Other manual changes after second perlimports run Bug 17600: Fix tests And a lot of other manual changes. export.pl is a dirty script that can be found on bug 17600. "perlimport" is: git clone https://github.com/oalders/App-perlimports.git cd App-perlimports/ cpanm --installdeps . export PERL5LIB="$PERL5LIB:/kohadevbox/koha/App-perlimports/lib" find . \( -name "*.pl" -o -name "*.pm" \) -exec perl App-perlimports/script/perlimports --inplace-edit --no-preserve-unused --filename {} \; The ideas of this patch are to: * use EXPORT_OK instead of EXPORT * perltidy the EXPORT_OK list * remove '&' before the subroutine names * remove some uneeded use statements * explicitely import the subroutines we need within the controllers or modules Note that the private subroutines (starting with _) should not be exported (and not used from outside of the module except from tests). EXPORT vs EXPORT_OK (from https://www.thegeekstuff.com/2010/06/perl-exporter-examples/) """ Export allows to export the functions and variables of modules to user’s namespace using the standard import method. This way, we don’t need to create the objects for the modules to access it’s members. @EXPORT and @EXPORT_OK are the two main variables used during export operation. @EXPORT contains list of symbols (subroutines and variables) of the module to be exported into the caller namespace. @EXPORT_OK does export of symbols on demand basis. """ If this patch caused a conflict with a patch you wrote prior to its push: * Make sure you are not reintroducing a "use" statement that has been removed * "$subroutine" is not exported by the C4::$MODULE module means that you need to add the subroutine to the @EXPORT_OK list * Bareword "$subroutine" not allowed while "strict subs" means that you didn't imported the subroutine from the module: - use $MODULE qw( $subroutine list ); You can also use the fully qualified namespace: C4::$MODULE::$subroutine Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
415 lines
14 KiB
Perl
Executable file
415 lines
14 KiB
Perl
Executable file
#!/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 MARC::File::XML;
|
|
use List::MoreUtils qw( uniq );
|
|
use Getopt::Long qw( GetOptions );
|
|
use Pod::Usage qw( pod2usage );
|
|
|
|
use Koha::Script;
|
|
use C4::Auth;
|
|
use C4::Context;
|
|
use C4::Record;
|
|
|
|
use Koha::Biblioitems;
|
|
use Koha::Database;
|
|
use Koha::CsvProfiles;
|
|
use Koha::Exporter::Record;
|
|
use Koha::DateUtils qw( dt_from_string output_pref );
|
|
|
|
my (
|
|
$output_format,
|
|
$timestamp,
|
|
$dont_export_items,
|
|
$csv_profile_id,
|
|
$deleted_barcodes,
|
|
$clean,
|
|
$filename,
|
|
$record_type,
|
|
$id_list_file,
|
|
$starting_authid,
|
|
$ending_authid,
|
|
$authtype,
|
|
$starting_biblionumber,
|
|
$ending_biblionumber,
|
|
$itemtype,
|
|
$starting_callnumber,
|
|
$ending_callnumber,
|
|
$start_accession,
|
|
$end_accession,
|
|
$marc_conditions,
|
|
$help
|
|
);
|
|
|
|
GetOptions(
|
|
'format=s' => \$output_format,
|
|
'date=s' => \$timestamp,
|
|
'dont_export_items' => \$dont_export_items,
|
|
'csv_profile_id=s' => \$csv_profile_id,
|
|
'deleted_barcodes' => \$deleted_barcodes,
|
|
'clean' => \$clean,
|
|
'filename=s' => \$filename,
|
|
'record-type=s' => \$record_type,
|
|
'id_list_file=s' => \$id_list_file,
|
|
'starting_authid=s' => \$starting_authid,
|
|
'ending_authid=s' => \$ending_authid,
|
|
'authtype=s' => \$authtype,
|
|
'starting_biblionumber=s' => \$starting_biblionumber,
|
|
'ending_biblionumber=s' => \$ending_biblionumber,
|
|
'itemtype=s' => \$itemtype,
|
|
'starting_callnumber=s' => \$starting_callnumber,
|
|
'ending_callnumber=s' => \$ending_callnumber,
|
|
'start_accession=s' => \$start_accession,
|
|
'end_accession=s' => \$end_accession,
|
|
'marc_conditions=s' => \$marc_conditions,
|
|
'h|help|?' => \$help
|
|
) || pod2usage(1);
|
|
|
|
if ($help) {
|
|
pod2usage(1);
|
|
}
|
|
|
|
$filename ||= 'koha.mrc';
|
|
$output_format ||= 'iso2709';
|
|
$record_type ||= 'bibs';
|
|
|
|
# Retrocompatibility for the format parameter
|
|
$output_format = 'iso2709' if $output_format eq 'marc';
|
|
|
|
if ( $output_format eq 'csv' and $record_type eq 'auths' ) {
|
|
pod2usage(q|CSV output is only available for biblio records|);
|
|
}
|
|
|
|
if ( $output_format eq 'csv' and not $csv_profile_id ) {
|
|
pod2usage(q|Define a csv profile to export in CSV|);
|
|
}
|
|
|
|
if ( $timestamp and $record_type ne 'bibs' ) {
|
|
pod2usage(q|--timestamp can only be used with biblios|);
|
|
}
|
|
|
|
if ( $record_type ne 'bibs' and $record_type ne 'auths' ) {
|
|
pod2usage(q|--record_type is not valid|);
|
|
}
|
|
|
|
if ( $deleted_barcodes and $record_type ne 'bibs' ) {
|
|
pod2usage(q|--deleted_barcodes can only be used with biblios|);
|
|
}
|
|
|
|
$start_accession = dt_from_string( $start_accession ) if $start_accession;
|
|
$end_accession = dt_from_string( $end_accession ) if $end_accession;
|
|
|
|
# Parse marc conditions
|
|
my @marc_conditions;
|
|
if ($marc_conditions) {
|
|
foreach my $condition (split(/,\s*/, $marc_conditions)) {
|
|
if ($condition =~ /^(\d{3})([\w\d]?)(=|(?:!=)|>|<)([^,]+)$/) {
|
|
push @marc_conditions, [$1, $2, $3, $4];
|
|
}
|
|
elsif ($condition =~ /^(exists|not_exists)\((\d{3})([\w\d]?)\)$/) {
|
|
push @marc_conditions, [$2, $3, $1 eq 'exists' ? '?' : '!?'];
|
|
}
|
|
else {
|
|
die("Invalid condititon: $condition");
|
|
}
|
|
}
|
|
}
|
|
|
|
my $dbh = C4::Context->dbh;
|
|
|
|
# Redirect stdout
|
|
open STDOUT, '>', $filename if $filename;
|
|
|
|
|
|
my @record_ids;
|
|
|
|
$timestamp = ($timestamp) ? output_pref({ dt => dt_from_string($timestamp), dateformat => 'iso', dateonly => 0, }): '';
|
|
|
|
if ( $record_type eq 'bibs' ) {
|
|
if ( $timestamp ) {
|
|
if (!$dont_export_items) {
|
|
push @record_ids, $_->{biblionumber} for @{
|
|
$dbh->selectall_arrayref(q| (
|
|
SELECT biblio_metadata.biblionumber
|
|
FROM biblio_metadata
|
|
LEFT JOIN items USING(biblionumber)
|
|
WHERE biblio_metadata.timestamp >= ?
|
|
OR items.timestamp >= ?
|
|
) UNION (
|
|
SELECT biblio_metadata.biblionumber
|
|
FROM biblio_metadata
|
|
LEFT JOIN deleteditems USING(biblionumber)
|
|
WHERE biblio_metadata.timestamp >= ?
|
|
OR deleteditems.timestamp >= ?
|
|
) |, { Slice => {} }, ( $timestamp ) x 4 );
|
|
};
|
|
} else {
|
|
push @record_ids, $_->{biblionumber} for @{
|
|
$dbh->selectall_arrayref(q| (
|
|
SELECT biblio_metadata.biblionumber
|
|
FROM biblio_metadata
|
|
WHERE biblio_metadata.timestamp >= ?
|
|
) |, { Slice => {} }, $timestamp );
|
|
};
|
|
}
|
|
} else {
|
|
my $conditions = {
|
|
( $starting_biblionumber or $ending_biblionumber )
|
|
? (
|
|
"me.biblionumber" => {
|
|
( $starting_biblionumber ? ( '>=' => $starting_biblionumber ) : () ),
|
|
( $ending_biblionumber ? ( '<=' => $ending_biblionumber ) : () ),
|
|
}
|
|
)
|
|
: (),
|
|
( $starting_callnumber or $ending_callnumber )
|
|
? (
|
|
callnumber => {
|
|
( $starting_callnumber ? ( '>=' => $starting_callnumber ) : () ),
|
|
( $ending_callnumber ? ( '<=' => $ending_callnumber ) : () ),
|
|
}
|
|
)
|
|
: (),
|
|
( $start_accession or $end_accession )
|
|
? (
|
|
dateaccessioned => {
|
|
( $start_accession ? ( '>=' => $start_accession ) : () ),
|
|
( $end_accession ? ( '<=' => $end_accession ) : () ),
|
|
}
|
|
)
|
|
: (),
|
|
( $itemtype
|
|
?
|
|
C4::Context->preference('item-level_itypes')
|
|
? ( 'items.itype' => $itemtype )
|
|
: ( 'me.itemtype' => $itemtype )
|
|
: ()
|
|
),
|
|
|
|
};
|
|
my $biblioitems = Koha::Biblioitems->search( $conditions, { join => 'items' } );
|
|
while ( my $biblioitem = $biblioitems->next ) {
|
|
push @record_ids, $biblioitem->biblionumber;
|
|
}
|
|
}
|
|
}
|
|
elsif ( $record_type eq 'auths' ) {
|
|
my $conditions = {
|
|
( $starting_authid or $ending_authid )
|
|
? (
|
|
authid => {
|
|
( $starting_authid ? ( '>=' => $starting_authid ) : () ),
|
|
( $ending_authid ? ( '<=' => $ending_authid ) : () ),
|
|
}
|
|
)
|
|
: (),
|
|
( $authtype ? ( authtypecode => $authtype ) : () ),
|
|
};
|
|
# Koha::MetadataRecord::Authority is not a Koha::Object...
|
|
my $authorities = Koha::Database->new->schema->resultset('AuthHeader')->search( $conditions );
|
|
@record_ids = map { $_->authid } $authorities->all;
|
|
}
|
|
|
|
@record_ids = uniq @record_ids;
|
|
if ( @record_ids and $id_list_file ) {
|
|
open my $fh, '<', $id_list_file or die "Cannot open file $id_list_file ($!)";
|
|
my @filter_record_ids = <$fh>;
|
|
@filter_record_ids = map { my $id = $_; $id =~ s/[\r\n]*$//; $id } @filter_record_ids;
|
|
# intersection
|
|
my %record_ids = map { $_ => 1 } @record_ids;
|
|
@record_ids = grep $record_ids{$_}, @filter_record_ids;
|
|
}
|
|
|
|
if ($deleted_barcodes) {
|
|
for my $record_id ( @record_ids ) {
|
|
my $barcode = $dbh->selectall_arrayref(q|
|
|
SELECT DISTINCT barcode
|
|
FROM deleteditems
|
|
WHERE deleteditems.biblionumber = ?
|
|
AND barcode IS NOT NULL AND barcode != ''
|
|
|, { Slice => {} }, $record_id );
|
|
say $_->{barcode} for @$barcode;
|
|
}
|
|
}
|
|
else {
|
|
Koha::Exporter::Record::export(
|
|
{ record_type => $record_type,
|
|
record_ids => \@record_ids,
|
|
record_conditions => @marc_conditions ? \@marc_conditions : undef,
|
|
format => $output_format,
|
|
csv_profile_id => $csv_profile_id,
|
|
export_items => (not $dont_export_items),
|
|
clean => $clean || 0,
|
|
}
|
|
);
|
|
}
|
|
exit;
|
|
|
|
|
|
=head1 NAME
|
|
|
|
export records - This script exports record (biblios or authorities)
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
export_records.pl [-h|--help] [--format=format] [--date=datetime] [--record-type=TYPE] [--dont_export_items] [--deleted_barcodes] [--clean] [--id_list_file=PATH] --filename=outputfile
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over
|
|
|
|
=item B<-h|--help>
|
|
|
|
Print a brief help message.
|
|
|
|
=item B<--format>
|
|
|
|
--format=FORMAT FORMAT is either 'xml', 'csv' (biblio records only) or 'marc' (default).
|
|
|
|
=item B<--date>
|
|
|
|
--date=DATETIME DATETIME should be entered as the 'dateformat' syspref is
|
|
set (dd/mm/yyyy[ hh:mm:ss] for metric, yyyy-mm-dd[ hh:mm:ss] for iso,
|
|
mm/dd/yyyy[ hh:mm:ss] for us) records exported are the ones that
|
|
have been modified since DATETIME.
|
|
|
|
=item B<--record-type>
|
|
|
|
--record-type=TYPE TYPE is 'bibs' or 'auths'.
|
|
|
|
=item B<--dont_export_items>
|
|
|
|
--dont_export_items If enabled, the item infos won't be exported.
|
|
|
|
=item B<--csv_profile_id>
|
|
|
|
--csv_profile_id=ID Generate a CSV file with the given CSV profile id (see tools/csv-profiles.pl)
|
|
This can only be used to export biblio records.
|
|
|
|
=item B<--deleted_barcodes>
|
|
|
|
--deleted_barcodes If used, a list of barcodes of items deleted since DATE
|
|
is produced (or from all deleted items if no date is
|
|
specified). Used only if TYPE is 'bibs'.
|
|
|
|
=item B<--clean>
|
|
|
|
--clean removes NSE/NSB.
|
|
|
|
=item B<--id_list_file>
|
|
|
|
--id_list_file=PATH PATH is a path to a file containing a list of
|
|
IDs (biblionumber or authid) with one ID per line.
|
|
This list works as a filter; it is compatible with
|
|
other parameters for selecting records.
|
|
|
|
=item B<--filename>
|
|
|
|
--filename=FILENAME FILENAME used to export the data.
|
|
|
|
=item B<--starting_authid>
|
|
|
|
--starting_authid=ID Export authorities with authid >= ID
|
|
|
|
=item B<--ending_authid>
|
|
|
|
--ending_authid=ID Export authorities with authid <= ID
|
|
|
|
=item B<--authtype>
|
|
|
|
--authtype=AUTHTYPE Export authorities from the given AUTHTYPE
|
|
|
|
=item B<--starting_biblionumber>
|
|
|
|
--starting_biblionumber=ID Export biblio with biblionumber >= ID
|
|
|
|
=item B<--ending_biblionumber>
|
|
|
|
--ending_biblionumber=ID Export biblio with biblionumber <= ID
|
|
|
|
=item B<--itemtype>
|
|
|
|
--itemtype=ITEMTYPE Export biblio from the given ITEMTYPE
|
|
|
|
=item B<--starting_callnumber>
|
|
|
|
--starting_callnumber=CALLNUMBER Export biblio with callnumber >=CALLNUMBER
|
|
|
|
=item B<--ending_callnumber>
|
|
|
|
--ending_callnumber=CALLNUMBER Export biblio with callnumber <=CALLNUMBER
|
|
|
|
=item B<--start_accession>
|
|
|
|
--starting_accession=DATE Export biblio with an item accessionned after DATE
|
|
|
|
=item B<--end_accession>
|
|
|
|
--end_accession=DATE Export biblio with an item accessionned after DATE
|
|
|
|
=item B<--marc_conditions>
|
|
|
|
--marc_conditions=CONDITIONS Only include biblios with MARC data matching CONDITIONS.
|
|
CONDITIONS is on the format: <marc_target><binary_operator><value>,
|
|
or <unary_operation>(<marc_target>).
|
|
with multiple conditions separated by commas (,).
|
|
For example: --marc_conditions="035a!=(EXAMPLE)123,041a=swe".
|
|
Multiple conditions are all required to match.
|
|
If <marc_target> has multiple values all values
|
|
are also required to match.
|
|
Valid operators are: = (equal to), != (not equal to),
|
|
> (great than) and < (less than).
|
|
|
|
Two unary operations are also supported:
|
|
exists(<marc_target>) and not_exists(<marc_target>).
|
|
For example: --marc_conditions="exists(035a)".
|
|
|
|
"exists(<marc_target)" will include marc records where
|
|
<marc_target> exists regardless of target value, and
|
|
"exists(<marc_target>)" will include marc records where
|
|
no <marc_target> exists.
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Koha Development Team
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright Koha Team
|
|
|
|
=head1 LICENSE
|
|
|
|
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>.
|
|
|
|
=cut
|