View | Details | Raw Unified | Return to bug 20486
Collapse All | Expand All

(-)a/Koha/Exporter/Record.pm (-5 / +58 lines)
Lines 9-19 use C4::Biblio; Link Here
9
use C4::Record;
9
use C4::Record;
10
use Koha::CsvProfiles;
10
use Koha::CsvProfiles;
11
use Koha::Logger;
11
use Koha::Logger;
12
use List::Util qw(all any);
12
13
13
sub _get_record_for_export {
14
sub _get_record_for_export {
14
    my ($params)           = @_;
15
    my ($params)           = @_;
15
    my $record_type        = $params->{record_type};
16
    my $record_type        = $params->{record_type};
16
    my $record_id          = $params->{record_id};
17
    my $record_id          = $params->{record_id};
18
    my $conditions         = $params->{record_conditions};
17
    my $dont_export_fields = $params->{dont_export_fields};
19
    my $dont_export_fields = $params->{dont_export_fields};
18
    my $clean              = $params->{clean};
20
    my $clean              = $params->{clean};
19
21
Lines 25-31 sub _get_record_for_export { Link Here
25
    } else {
27
    } else {
26
        Koha::Logger->get->warn( "Record_type $record_type not supported." );
28
        Koha::Logger->get->warn( "Record_type $record_type not supported." );
27
    }
29
    }
28
    return unless $record;
30
    if ( !$record ) {
31
        Koha::Logger->get->warn( "Record $record_id could not be exported." );
32
        return;
33
    }
34
35
    # If multiple conditions all are required to match (and)
36
    # For matching against multiple marc targets all are also required to match
37
    my %operators = (
38
        '=' => sub {
39
            return $_[0] eq $_[1];
40
        },
41
        '!=' => sub {
42
            return $_[0] ne $_[1];
43
        },
44
        '>' => sub {
45
            return $_[0] gt $_[1];
46
        },
47
        '<' => sub {
48
            return $_[0] lt $_[1];
49
        },
50
    );
51
    if ($conditions) {
52
        foreach my $condition (@{$conditions}) {
53
            my ($field_tag, $subfield, $operator, $match_value) = @{$condition};
54
            my @fields = $record->field($field_tag);
55
            my $no_target = 0;
56
57
            if (!@fields) {
58
                $no_target = 1;
59
            }
60
            else {
61
                if ($operator eq '?') {
62
                    return unless any { $subfield ? $_->subfield($subfield) : $_->data() } @fields;
63
                } elsif ($operator eq '!?') {
64
                    return if any { $subfield ? $_->subfield($subfield) : $_->data() } @fields;
65
                } else {
66
                    my $op;
67
                    if (exists $operators{$operator}) {
68
                        $op = $operators{$operator};
69
                    } else {
70
                        die("Invalid operator: $op");
71
                    }
72
                    my @target_values = map { $subfield ? $_->subfield($subfield) : ($_->data()) } @fields;
73
                    if (!@target_values) {
74
                        $no_target = 1;
75
                    }
76
                    else {
77
                        return unless all { $op->($_, $match_value) } @target_values;
78
                    }
79
                }
80
            }
81
            return if $no_target && $operator ne '!=';
82
        }
83
    }
29
84
30
    if ($dont_export_fields) {
85
    if ($dont_export_fields) {
31
        for my $f ( split / /, $dont_export_fields ) {
86
        for my $f ( split / /, $dont_export_fields ) {
Lines 116-121 sub export { Link Here
116
    if ( $format eq 'iso2709' ) {
171
    if ( $format eq 'iso2709' ) {
117
        for my $record_id (@$record_ids) {
172
        for my $record_id (@$record_ids) {
118
            my $record = _get_record_for_export( { %$params, record_id => $record_id } );
173
            my $record = _get_record_for_export( { %$params, record_id => $record_id } );
174
            next unless $record;
119
            my $errorcount_on_decode = eval { scalar( MARC::File::USMARC->decode( $record->as_usmarc )->warnings() ) };
175
            my $errorcount_on_decode = eval { scalar( MARC::File::USMARC->decode( $record->as_usmarc )->warnings() ) };
120
            if ( $errorcount_on_decode or $@ ) {
176
            if ( $errorcount_on_decode or $@ ) {
121
                my $msg = "Record $record_id could not be exported. " .
177
                my $msg = "Record $record_id could not be exported. " .
Lines 134-143 sub export { Link Here
134
        print "\n";
190
        print "\n";
135
        for my $record_id (@$record_ids) {
191
        for my $record_id (@$record_ids) {
136
            my $record = _get_record_for_export( { %$params, record_id => $record_id } );
192
            my $record = _get_record_for_export( { %$params, record_id => $record_id } );
137
            if( !$record ) {
193
            next unless $record;
138
                Koha::Logger->get->info( "Record $record_id could not be exported." );
139
                next;
140
            }
141
            print MARC::File::XML::record($record);
194
            print MARC::File::XML::record($record);
142
            print "\n";
195
            print "\n";
143
        }
196
        }
(-)a/misc/export_records.pl (-1 / +64 lines)
Lines 32-38 use Koha::CsvProfiles; Link Here
32
use Koha::Exporter::Record;
32
use Koha::Exporter::Record;
33
use Koha::DateUtils qw( dt_from_string output_pref );
33
use Koha::DateUtils qw( dt_from_string output_pref );
34
34
35
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, $help );
35
my (
36
    $output_format,
37
    $timestamp,
38
    $dont_export_items,
39
    $csv_profile_id,
40
    $deleted_barcodes,
41
    $clean,
42
    $filename,
43
    $record_type,
44
    $id_list_file,
45
    $starting_authid,
46
    $ending_authid,
47
    $authtype,
48
    $starting_biblionumber,
49
    $ending_biblionumber,
50
    $itemtype,
51
    $starting_callnumber,
52
    $ending_callnumber,
53
    $start_accession,
54
    $end_accession,
55
    $marc_conditions,
56
    $help
57
);
58
36
GetOptions(
59
GetOptions(
37
    'format=s'                => \$output_format,
60
    'format=s'                => \$output_format,
38
    'date=s'                  => \$timestamp,
61
    'date=s'                  => \$timestamp,
Lines 53-58 GetOptions( Link Here
53
    'ending_callnumber=s'     => \$ending_callnumber,
76
    'ending_callnumber=s'     => \$ending_callnumber,
54
    'start_accession=s'       => \$start_accession,
77
    'start_accession=s'       => \$start_accession,
55
    'end_accession=s'         => \$end_accession,
78
    'end_accession=s'         => \$end_accession,
79
    'marc_conditions=s'       => \$marc_conditions,
56
    'h|help|?'                => \$help
80
    'h|help|?'                => \$help
57
) || pod2usage(1);
81
) || pod2usage(1);
58
82
Lines 90-95 if ( $deleted_barcodes and $record_type ne 'bibs' ) { Link Here
90
$start_accession = dt_from_string( $start_accession ) if $start_accession;
114
$start_accession = dt_from_string( $start_accession ) if $start_accession;
91
$end_accession   = dt_from_string( $end_accession )   if $end_accession;
115
$end_accession   = dt_from_string( $end_accession )   if $end_accession;
92
116
117
# Parse marc conditions
118
my @marc_conditions;
119
if ($marc_conditions) {
120
    foreach my $condition (split(/,\s*/, $marc_conditions)) {
121
        if ($condition =~ /^(\d{3})([\w\d]?)(=|(?:!=)|>|<)([^,]+)$/) {
122
            push @marc_conditions, [$1, $2, $3, $4];
123
        }
124
        elsif ($condition =~ /^(exists|not_exists)\((\d{3})([\w\d]?)\)$/) {
125
            push @marc_conditions, [$2, $3, $1 eq 'exists' ? '?' : '!?'];
126
        }
127
        else {
128
            die("Invalid condititon: $condition");
129
        }
130
    }
131
}
132
93
my $dbh = C4::Context->dbh;
133
my $dbh = C4::Context->dbh;
94
134
95
# Redirect stdout
135
# Redirect stdout
Lines 199-204 else { Link Here
199
    Koha::Exporter::Record::export(
239
    Koha::Exporter::Record::export(
200
        {   record_type        => $record_type,
240
        {   record_type        => $record_type,
201
            record_ids         => \@record_ids,
241
            record_ids         => \@record_ids,
242
            record_conditions  => @marc_conditions ? \@marc_conditions : undef,
202
            format             => $output_format,
243
            format             => $output_format,
203
            csv_profile_id     => $csv_profile_id,
244
            csv_profile_id     => $csv_profile_id,
204
            export_items       => (not $dont_export_items),
245
            export_items       => (not $dont_export_items),
Lines 310-315 Print a brief help message. Link Here
310
351
311
 --end_accession=DATE           Export biblio with an item accessionned after DATE
352
 --end_accession=DATE           Export biblio with an item accessionned after DATE
312
353
354
=item B<--marc_conditions>
355
356
 --marc_conditions=CONDITIONS   Only include biblios with MARC data matching CONDITIONS.
357
                                CONDITIONS is on the format: <marc_target><binary_operator><value>,
358
                                or <unary_operation>(<marc_target>).
359
                                with multiple conditions separated by commas (,).
360
                                For example: --marc_conditions="035a!=(EXAMPLE)123,041a=swe".
361
                                Multiple conditions are all required to match.
362
                                If <marc_target> has multiple values all values
363
                                are also required to match.
364
                                Valid operators are: = (equal to), != (not equal to),
365
                                > (great than) and < (less than).
366
367
                                Two unary operations are also supported:
368
                                exists(<marc_target>) and not_exists(<marc_target>).
369
                                For example: --marc_conditions="exists(035a)".
370
371
                                "exists(<marc_target)" will include marc records where
372
                                <marc_target> exists regardless of target value, and
373
                                "exists(<marc_target>)" will include marc records where
374
                                no <marc_target> exists.
375
313
=back
376
=back
314
377
315
=head1 AUTHOR
378
=head1 AUTHOR
(-)a/t/db_dependent/Exporter/Record.t (-3 / +123 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 5;
20
use Test::More tests => 6;
21
use Test::Warn;
21
use Test::Warn;
22
use t::lib::TestBuilder;
22
use t::lib::TestBuilder;
23
23
Lines 281-288 subtest '_get_biblio_for_export' => sub { Link Here
281
281
282
};
282
};
283
283
284
subtest '_get_record_for_export MARC field conditions' => sub {
285
    plan tests => 11;
284
286
287
    my $biblio = MARC::Record->new();
288
    $biblio->leader('00266nam a22001097a 4500');
289
    $biblio->append_fields(
290
        MARC::Field->new( '100', ' ', ' ', a => 'Thurber, James' ),
291
        MARC::Field->new( '245', ' ', ' ', a => 'The 13 Clocks' ),
292
        MARC::Field->new( '080', ' ', ' ', a => '12345' ),
293
        MARC::Field->new( '035', ' ', ' ', a => '(TEST)123' ),
294
        MARC::Field->new( '035', ' ', ' ', a => '(TEST)1234' ),
295
    );
296
    my ( $biblionumber ) = AddBiblio( $biblio, '' );
297
    my $record;
285
298
299
    $record = Koha::Exporter::Record::_get_record_for_export(
300
        {
301
            record_id => $biblionumber,
302
            record_conditions => [['080', 'a', '=', '12345']],
303
            record_type => 'bibs',
304
        }
305
    );
306
    ok( $record, "Record condition \"080a=12345\" should match" );
286
307
287
$schema->storage->txn_rollback;
308
    $record = Koha::Exporter::Record::_get_record_for_export(
309
        {
310
            record_id => $biblionumber,
311
            record_conditions => [['080', 'a', '!=', '12345']],
312
            record_type => 'bibs',
313
        }
314
    );
315
    is( $record, undef, "Record condition \"080a!=12345\" should not match" );
316
317
    $record = Koha::Exporter::Record::_get_record_for_export(
318
        {
319
            record_id => $biblionumber,
320
            record_conditions => [['080', 'a', '>', '1234']],
321
            record_type => 'bibs',
322
        }
323
    );
324
    ok( $record, "Record condition \"080a>1234\" should match" );
325
326
    $record = Koha::Exporter::Record::_get_record_for_export(
327
        {
328
            record_id => $biblionumber,
329
            record_conditions => [['080', 'a', '<', '123456']],
330
            record_type => 'bibs',
331
        }
332
    );
333
    ok( $record, "Record condition \"080a<123456\" should match" );
334
335
    $record = Koha::Exporter::Record::_get_record_for_export(
336
        {
337
            record_id => $biblionumber,
338
            record_conditions => [['080', 'a', '>', '123456']],
339
            record_type => 'bibs',
340
        }
341
    );
342
    is( $record, undef, "Record condition \"080a>123456\" should not match" );
343
344
345
    ## Multiple subfields
346
347
    $record = Koha::Exporter::Record::_get_record_for_export(
348
        {
349
            record_id => $biblionumber,
350
            record_conditions => [['035', 'a', '!=', 'TEST(12345)']],
351
            record_type => 'bibs',
352
        }
353
    );
354
    ok( $record, "Record condition \"035a!=TEST(12345)\" should match" );
355
356
    $record = Koha::Exporter::Record::_get_record_for_export(
357
        {
358
            record_id => $biblionumber,
359
            record_conditions => [['035', 'a', '=', 'TEST(1234)']],
360
            record_type => 'bibs',
361
        }
362
    );
363
    is( $record, undef, "Record condition \"035a=TEST(1234)\" should not match" ); # Since matching all subfields required
364
365
366
    ## Multiple conditions
367
368
    $record = Koha::Exporter::Record::_get_record_for_export(
369
        {
370
            record_id => $biblionumber,
371
            record_conditions => [['035', 'a', '!=', 'TEST(12345)'], ['080', 'a', '>', '1234']],
372
            record_type => 'bibs',
373
        }
374
    );
375
    ok( $record, "Record condition \"035a!=TEST(12345),080a>1234\" should match" );
376
377
    $record = Koha::Exporter::Record::_get_record_for_export(
378
        {
379
            record_id => $biblionumber,
380
            record_conditions => [['035', 'a', '!=', 'TEST(12345)'], ['080', 'a', '<', '1234']],
381
            record_type => 'bibs',
382
        }
383
    );
384
    is( $record, undef, "Record condition \"035a!=TEST(12345),080a<1234\" should not match" );
385
386
387
    ## exists/not_exists
388
389
    $record = Koha::Exporter::Record::_get_record_for_export(
390
        {
391
            record_id => $biblionumber,
392
            record_conditions => [['035', 'a', '?']],
393
            record_type => 'bibs',
394
        }
395
    );
396
    ok( $record, "Record condition \"exists(035a)\" should match" );
288
397
289
- 
398
    $record = Koha::Exporter::Record::_get_record_for_export(
399
        {
400
            record_id => $biblionumber,
401
            record_conditions => [['035', 'a', '!?']],
402
            record_type => 'bibs',
403
            record_type => 'bibs',
404
        }
405
    );
406
    is( $record, undef, "Record condition \"not_exists(035a)\" should not match" );
407
};
408
409
$schema->storage->txn_rollback;

Return to bug 20486