Lines 16-22
Link Here
|
16 |
# along with Koha; if not, see <https://www.gnu.org/licenses>. |
16 |
# along with Koha; if not, see <https://www.gnu.org/licenses>. |
17 |
|
17 |
|
18 |
use Modern::Perl; |
18 |
use Modern::Perl; |
19 |
|
|
|
20 |
use Koha::Script; |
19 |
use Koha::Script; |
21 |
use Koha::AuthorisedValues; |
20 |
use Koha::AuthorisedValues; |
22 |
use Koha::Authorities; |
21 |
use Koha::Authorities; |
Lines 27-52
use Koha::Items;
Link Here
|
27 |
use Koha::ItemTypes; |
26 |
use Koha::ItemTypes; |
28 |
use Koha::Patrons; |
27 |
use Koha::Patrons; |
29 |
use C4::Biblio qw( GetMarcFromKohaField ); |
28 |
use C4::Biblio qw( GetMarcFromKohaField ); |
|
|
29 |
use Getopt::Long; |
30 |
use Pod::Usage; |
31 |
|
32 |
our %options = ( |
33 |
'check-branch' => 0, |
34 |
'check-auth' => 0, |
35 |
'check-status' => 0, |
36 |
'check-framework' => 0, |
37 |
'check-title' => 0, |
38 |
'check-age' => 0, |
39 |
'skip-branch' => 1, |
40 |
'skip-auth' => 1, |
41 |
'skip-status' => 1, |
42 |
'skip-framework' => 1, |
43 |
'skip-title' => 1, |
44 |
'skip-age' => 1, |
45 |
'help' => 0, |
46 |
); |
47 |
|
48 |
# Parse command line options |
49 |
GetOptions( |
50 |
'check-branch' => sub { $options{'check-branch'} = 1 }, |
51 |
'check-auth' => sub { $options{'check-auth'} = 1 }, |
52 |
'check-status' => sub { $options{'check-status'} = 1 }, |
53 |
'check-framework' => sub { $options{'check-framework'} = 1 }, |
54 |
'check-title' => sub { $options{'check-title'} = 1 }, |
55 |
'check-age' => sub { $options{'check-age'} = 1 }, |
56 |
'skip-branch' => sub { $options{'skip-branch'} = 0 }, |
57 |
'skip-auth' => sub { $options{'skip-auth'} = 0 }, |
58 |
'skip-status' => sub { $options{'skip-status'} = 0 }, |
59 |
'skip-framework' => sub { $options{'skip-framework'} = 0 }, |
60 |
'skip-title' => sub { $options{'skip-title'} = 0 }, |
61 |
'skip-age' => sub { $options{'skip-age'} = 0 }, |
62 |
'help' => sub { $options{'help'} = 1; }, |
63 |
) or pod2usage(2); # Print usage if invalid options are provided |
64 |
|
65 |
# Call handle_invalid_options subroutine if invalid options are provided |
66 |
Getopt::Long::Configure('pass_through'); |
67 |
GetOptions( |
68 |
'<>' => sub { |
69 |
my ($opt_name) = @_; |
70 |
handle_invalid_options($opt_name); |
71 |
} |
72 |
); |
73 |
|
74 |
# Print usage and exit if --help flag is provided |
75 |
pod2usage(1) if $options{'help'}; |
76 |
|
77 |
# Run tests based on options |
78 |
set_skip_options(); |
79 |
CheckItemsBranch() if $options{'check-branch'} || $options{'skip-branch'}; |
80 |
CheckItemsAuthHeader() if $options{'check-auth'} || $options{'skip-auth'}; |
81 |
CheckItemsStatus() if $options{'check-status'} || $options{'skip-status'}; |
82 |
CheckItemsFramework() if $options{'check-framework'} || $options{'skip-framework'}; |
83 |
CheckItemsTitle() if $options{'check-title'} || $options{'skip-title'}; |
84 |
CheckAgeForCategory() if $options{'check-age'} || $options{'skip-age'}; |
85 |
|
86 |
# Handle invalid options |
87 |
sub handle_invalid_options { |
88 |
my ($opt_name) = @_; |
89 |
print "Invalid option provided: $opt_name\n"; |
90 |
pod2usage(2); |
91 |
} |
30 |
|
92 |
|
31 |
{ |
93 |
# Set skip options based on check options |
|
|
94 |
sub set_skip_options { |
95 |
my $has_check_option = grep { $options{$_} == 1 } grep { /^check-/ } keys %options; |
96 |
if ($has_check_option) { |
97 |
foreach my $key ( keys %options ) { |
98 |
$options{$key} = 0 if $key =~ /^skip-/; |
99 |
} |
100 |
} |
101 |
return %options; |
102 |
} |
103 |
|
104 |
sub CheckItemsBranch { |
32 |
my $items = Koha::Items->search( { -or => { homebranch => undef, holdingbranch => undef } } ); |
105 |
my $items = Koha::Items->search( { -or => { homebranch => undef, holdingbranch => undef } } ); |
33 |
if ( $items->count ) { new_section("Not defined items.homebranch and/or items.holdingbranch") } |
106 |
if ( $items->count ) { new_section("Not defined items.homebranch and/or items.holdingbranch") } |
34 |
while ( my $item = $items->next ) { |
107 |
while ( my $item = $items->next ) { |
35 |
if ( not $item->homebranch and not $item->holdingbranch ) { |
108 |
if ( not $item->homebranch and not $item->holdingbranch ) { |
36 |
new_item( |
109 |
new_item( |
37 |
sprintf "Item with itemnumber=%s does not have homebranch and holdingbranch defined", |
110 |
sprintf |
38 |
$item->itemnumber |
111 |
"Item with itemnumber=%s and biblionumber=%s does not have homebranch and holdingbranch defined", |
|
|
112 |
$item->itemnumber, $item->biblionumber |
39 |
); |
113 |
); |
40 |
} elsif ( not $item->homebranch ) { |
114 |
} elsif ( not $item->homebranch ) { |
41 |
new_item( sprintf "Item with itemnumber=%s does not have homebranch defined", $item->itemnumber ); |
115 |
new_item( |
|
|
116 |
sprintf "Item with itemnumber=%s and biblionumber=%s does not have homebranch defined", |
117 |
$item->itemnumber, $item->biblionumber |
118 |
); |
42 |
} else { |
119 |
} else { |
43 |
new_item( sprintf "Item with itemnumber=%s does not have holdingbranch defined", $item->itemnumber ); |
120 |
new_item( |
|
|
121 |
sprintf "Item with itemnumber=%s and biblionumber=%s does not have holdingbranch defined", |
122 |
$item->itemnumber, $item->biblionumber |
123 |
); |
44 |
} |
124 |
} |
45 |
} |
125 |
} |
46 |
if ( $items->count ) { new_hint("Edit these items and set valid homebranch and/or holdingbranch") } |
126 |
if ( $items->count ) { new_hint("Edit these items and set valid homebranch and/or holdingbranch") } |
47 |
} |
127 |
} |
48 |
|
128 |
|
49 |
{ |
129 |
sub CheckItemsAuthHeader { |
|
|
130 |
|
50 |
# No join possible, FK is missing at DB level |
131 |
# No join possible, FK is missing at DB level |
51 |
my @auth_types = Koha::Authority::Types->search->get_column('authtypecode'); |
132 |
my @auth_types = Koha::Authority::Types->search->get_column('authtypecode'); |
52 |
my $authorities = Koha::Authorities->search( { authtypecode => { 'not in' => \@auth_types } } ); |
133 |
my $authorities = Koha::Authorities->search( { authtypecode => { 'not in' => \@auth_types } } ); |
Lines 57-66
use C4::Biblio qw( GetMarcFromKohaField );
Link Here
|
57 |
$authority->authtypecode |
138 |
$authority->authtypecode |
58 |
); |
139 |
); |
59 |
} |
140 |
} |
60 |
if ( $authorities->count ) { new_hint("Go to 'Home › Administration › Authority types' to define them") } |
141 |
if ( $authorities->count ) { new_hint("Go to 'Home -> Administration -> Authority types' to define them") } |
61 |
} |
142 |
} |
62 |
|
143 |
|
63 |
{ |
144 |
sub CheckItemsStatus { |
64 |
if ( C4::Context->preference('item-level_itypes') ) { |
145 |
if ( C4::Context->preference('item-level_itypes') ) { |
65 |
my $items_without_itype = Koha::Items->search( { -or => [ itype => undef, itype => '' ] } ); |
146 |
my $items_without_itype = Koha::Items->search( { -or => [ itype => undef, itype => '' ] } ); |
66 |
if ( $items_without_itype->count ) { |
147 |
if ( $items_without_itype->count ) { |
Lines 69-82
use C4::Biblio qw( GetMarcFromKohaField );
Link Here
|
69 |
if ( defined $item->biblioitem->itemtype && $item->biblioitem->itemtype ne '' ) { |
150 |
if ( defined $item->biblioitem->itemtype && $item->biblioitem->itemtype ne '' ) { |
70 |
new_item( |
151 |
new_item( |
71 |
sprintf |
152 |
sprintf |
72 |
"Item with itemnumber=%s does not have a itype value, biblio's item type will be used (%s)", |
153 |
"Item with itemnumber=%s and biblionumber = %s does not have a itype value, biblio's item type will be used (%s)", |
73 |
$item->itemnumber, $item->biblioitem->itemtype |
154 |
$item->itemnumber, $item->biblioitem->biblionumber, $item->biblioitem->itemtype |
74 |
); |
155 |
); |
75 |
} else { |
156 |
} else { |
76 |
new_item( |
157 |
new_item( |
77 |
sprintf |
158 |
sprintf |
78 |
"Item with itemnumber=%s does not have a itype value, additionally no item type defined for biblionumber=%s", |
159 |
"Item with itemnumber=%s and biblionumber = %s does not have a itype value, additionally no item type defined for biblionumber=%s", |
79 |
$item->itemnumber, $item->biblioitem->biblionumber |
160 |
$item->itemnumber, $item->biblioitem->biblionumber, $item->biblioitem->biblionumber |
80 |
); |
161 |
); |
81 |
} |
162 |
} |
82 |
} |
163 |
} |
Lines 229-235
use C4::Biblio qw( GetMarcFromKohaField );
Link Here
|
229 |
} |
310 |
} |
230 |
} |
311 |
} |
231 |
|
312 |
|
232 |
{ |
313 |
sub CheckItemsFramework { |
233 |
my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode'); |
314 |
my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode'); |
234 |
push @framework_codes, ""; # The default is not stored in frameworks, we need to force it |
315 |
push @framework_codes, ""; # The default is not stored in frameworks, we need to force it |
235 |
|
316 |
|
Lines 294-305
use C4::Biblio qw( GetMarcFromKohaField );
Link Here
|
294 |
$table eq 'items' ? $i->$column |
375 |
$table eq 'items' ? $i->$column |
295 |
: $table eq 'biblio' ? $i->biblio->$column |
376 |
: $table eq 'biblio' ? $i->biblio->$column |
296 |
: $i->biblioitem->$column; |
377 |
: $i->biblioitem->$column; |
297 |
$output .= " {" . $i->itemnumber . " => " . $value . "}\n"; |
378 |
$output .= " {" . $i->itemnumber . " and " . $i->biblioitem->biblionumber . " => " . $value . "}\n"; |
298 |
} |
379 |
} |
299 |
new_item( |
380 |
new_item( |
300 |
sprintf( |
381 |
sprintf( |
301 |
"The Framework *%s* is using the authorised value's category *%s*, " |
382 |
"The Framework *%s* is using the authorised value's category *%s*, " |
302 |
. "but the following %s do not have a value defined ({itemnumber => value }):\n%s", |
383 |
. "but the following %s do not have a value defined ({itemnumber and biblionumber => value }):\n%s", |
303 |
$frameworkcode, $av_category, $kohafield, $output |
384 |
$frameworkcode, $av_category, $kohafield, $output |
304 |
) |
385 |
) |
305 |
); |
386 |
); |
Lines 308-314
use C4::Biblio qw( GetMarcFromKohaField );
Link Here
|
308 |
} |
389 |
} |
309 |
} |
390 |
} |
310 |
|
391 |
|
311 |
{ |
392 |
sub CheckItemsTitle { |
312 |
my $biblios = Koha::Biblios->search( |
393 |
my $biblios = Koha::Biblios->search( |
313 |
{ |
394 |
{ |
314 |
-or => [ |
395 |
-or => [ |
Lines 329-335
use C4::Biblio qw( GetMarcFromKohaField );
Link Here
|
329 |
} |
410 |
} |
330 |
} |
411 |
} |
331 |
|
412 |
|
332 |
{ |
413 |
sub CheckAgeForCategory { |
333 |
my $aging_patrons = Koha::Patrons->search( |
414 |
my $aging_patrons = Koha::Patrons->search( |
334 |
{ |
415 |
{ |
335 |
-not => { |
416 |
-not => { |
Lines 502-527
sub new_hint {
Link Here
|
502 |
say "=> $name"; |
583 |
say "=> $name"; |
503 |
} |
584 |
} |
504 |
|
585 |
|
505 |
=head1 NAME |
|
|
506 |
|
507 |
search_for_data_inconsistencies.pl |
508 |
|
509 |
=head1 SYNOPSIS |
586 |
=head1 SYNOPSIS |
510 |
|
587 |
|
511 |
perl search_for_data_inconsistencies.pl |
588 |
search_for_data_inconsistencies.pl [options] |
512 |
|
589 |
|
513 |
=head1 DESCRIPTION |
590 |
=head2 DESCRIPTION |
514 |
|
591 |
|
515 |
Catch data inconsistencies in Koha database |
592 |
Catch data inconsistencies in Koha database: |
516 |
|
593 |
|
517 |
* Items with undefined homebranch and/or holdingbranch |
594 |
* Items with undefined homebranch and/or holdingbranch |
518 |
* Authorities with undefined authtypecodes/authority types |
595 |
* Authorities with undefined authtypecodes/authority types |
519 |
* Item types: |
596 |
* Item types: |
520 |
* if item types are defined at item level (item-level_itypes=specific item), |
597 |
* if item types are defined at item level (item-level_itypes=specific item), |
521 |
then items.itype must be set else biblioitems.itemtype must be set |
598 |
then items.itype must be set else biblioitems.itemtype must be set |
522 |
* Item types defined in items or biblioitems must be defined in the itemtypes table |
599 |
* Item types defined in items or biblioitems must be defined in the itemtypes table |
523 |
* Invalid MARCXML in bibliographic records |
600 |
* Bibliographic records without a title |
524 |
* Patrons with invalid category types due to lower and upper age limits |
601 |
* Invalid MARCXML in bibliographic records |
|
|
602 |
* Patrons with invalid category types due to lower and upper age limits |
525 |
* Any date fields in the database (timestamp, datetime, date) set to 0000-00-00 |
603 |
* Any date fields in the database (timestamp, datetime, date) set to 0000-00-00 |
526 |
|
604 |
|
|
|
605 |
=head2 OPTIONS |
606 |
|
607 |
--check-branch Check for items without home or holding library |
608 |
--check-auth Check for authority records with invalid authority type |
609 |
--check-status Check for bibliographic records and items without an item type or with an invalid item type |
610 |
--check-framework Check for invalid values in fields where the framework limits to an authorized value category |
611 |
--check-title Check for bibliographic records without a title |
612 |
--check-age Check for patrons with invalid age for category |
613 |
--skip-branch Skip checking for items without home or holding library |
614 |
--skip-auth Skip checking for authority records with invalid authority type |
615 |
--skip-status Skip checking for bibliographic records and items without an item type or with an invalid item type |
616 |
--skip-framework Skip checking for invalid values in fields where the framework limits to an authorized value category |
617 |
--skip-title Skip checking for bibliographic records without a title |
618 |
--skip-age Skip checking for patrons with invalid age for category |
619 |
--help Print usage information |
620 |
|
621 |
Note: If no options are provided, all tests will be run. |
622 |
|
527 |
=cut |
623 |
=cut |
528 |
- |
|
|