From ffbdb7e305b16fc0555a85c6416ebd28b26466e3 Mon Sep 17 00:00:00 2001 From: Pedro Amorim Date: Mon, 18 Aug 2025 12:19:19 +0000 Subject: [PATCH] [24.11.x] Bug 39345: 24.11 SQUASH prove t/db_dependent/api/v1/erm_usa* prove t/db_dependent/Koha/ERM/EUsage/* prove t/db_dependent/Koha/BackgroundJobs/ErmSushiHarvester.t prove t/db_dependent/api/v1/erm_custom_reports.t Signed-off-by: Eric Phetteplace Signed-off-by: Nick Clemens --- Koha/ERM/EUsage/COUNTER/5.pm | 335 ++++++ Koha/ERM/EUsage/COUNTER/5_1.pm | 468 ++++++++ Koha/ERM/EUsage/CounterFile.pm | 62 +- Koha/ERM/EUsage/SushiCounter.pm | 314 +---- Koha/ERM/EUsage/UsageDataProvider.pm | 61 +- Koha/Exceptions/ERM/EUsage/CounterFile.pm | 3 +- Koha/REST/V1/ERM/EUsage/CounterRegistry.pm | 10 +- .../Koha/BackgroundJobs/ErmSushiHarvester.t | 20 +- .../Koha/ERM/EUsage/COUNTER_5.1/CounterFile.t | 133 +++ .../ERM/EUsage/COUNTER_5.1/SushiCounter.t | 1031 +++++++++++++++++ .../ERM/EUsage/{ => COUNTER_5}/CounterFile.t | 2 +- .../ERM/EUsage/{ => COUNTER_5}/SushiCounter.t | 13 +- .../Koha/ERM/EUsage/UsageDataProvider.t | 43 +- t/db_dependent/api/v1/erm_custom_reports.t | 367 +++++- .../data/erm/eusage/COUNTER_5.1/TR_B2.json | 101 ++ .../data/erm/eusage/COUNTER_5.1/TR_B3.json | 147 +++ .../data/erm/eusage/COUNTER_5.1/TR_J1.json | 104 ++ .../data/erm/eusage/COUNTER_5.1/TR_J2.json | 96 ++ .../data/erm/eusage/COUNTER_5.1/TR_J3.json | 170 +++ .../data/erm/eusage/COUNTER_5.1/TR_J4.json | 83 ++ .../erm/eusage/{ => COUNTER_5}/TR_B2.json | 0 .../erm/eusage/{ => COUNTER_5}/TR_B3.json | 0 .../erm/eusage/{ => COUNTER_5}/TR_J1.json | 0 .../erm/eusage/{ => COUNTER_5}/TR_J2.json | 0 .../erm/eusage/{ => COUNTER_5}/TR_J3.json | 0 .../erm/eusage/{ => COUNTER_5}/TR_J4.json | 0 .../UnsupportedCOUNTER.json | 101 ++ 27 files changed, 3330 insertions(+), 334 deletions(-) create mode 100644 Koha/ERM/EUsage/COUNTER/5.pm create mode 100644 Koha/ERM/EUsage/COUNTER/5_1.pm create mode 100755 t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/CounterFile.t create mode 100755 t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/SushiCounter.t rename t/db_dependent/Koha/ERM/EUsage/{ => COUNTER_5}/CounterFile.t (99%) rename t/db_dependent/Koha/ERM/EUsage/{ => COUNTER_5}/SushiCounter.t (98%) create mode 100644 t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B2.json create mode 100644 t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B3.json create mode 100644 t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J1.json create mode 100644 t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J2.json create mode 100644 t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J3.json create mode 100644 t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J4.json rename t/db_dependent/data/erm/eusage/{ => COUNTER_5}/TR_B2.json (100%) rename t/db_dependent/data/erm/eusage/{ => COUNTER_5}/TR_B3.json (100%) rename t/db_dependent/data/erm/eusage/{ => COUNTER_5}/TR_J1.json (100%) rename t/db_dependent/data/erm/eusage/{ => COUNTER_5}/TR_J2.json (100%) rename t/db_dependent/data/erm/eusage/{ => COUNTER_5}/TR_J3.json (100%) rename t/db_dependent/data/erm/eusage/{ => COUNTER_5}/TR_J4.json (100%) create mode 100644 t/db_dependent/data/erm/eusage/UnsupportedCOUNTER/UnsupportedCOUNTER.json diff --git a/Koha/ERM/EUsage/COUNTER/5.pm b/Koha/ERM/EUsage/COUNTER/5.pm new file mode 100644 index 00000000000..e4dd98ec591 --- /dev/null +++ b/Koha/ERM/EUsage/COUNTER/5.pm @@ -0,0 +1,335 @@ +package Koha::ERM::EUsage::COUNTER::5; + +# Copyright 2025 Open Fifth + +# 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 . + +use Modern::Perl; + +use base qw(Koha::ERM::EUsage::SushiCounter); + +=head1 NAME + +Koha::ERM::EUsage::COUNTER::5 - Koha COUNTER 5 Object class + +=head1 API + +=head2 Class Methods + +=head3 new + + my $sushi_counter = + Koha::ERM::EUsage::SushiCounter->new( { response => decode_json( $response->decoded_content ) } ); + +=cut + +sub new { + my ($class) = @_; + my $self = {}; + + bless( $self, $class ); +} + +=head3 _COUNTER_report_header + +Return a COUNTER report header +https://cop5.projectcounter.org/en/5.0.2/04-reports/03-title-reports.html + +=cut + +sub _COUNTER_report_header { + my ($self) = @_; + + my $header = $self->{sushi}->{header}; + + my @metric_types_string = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Metric_Type" ); + + my $begin_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Begin_Date" ); + my $end_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "End_Date" ); + + return ( + [ Report_Name => $header->{Report_Name} || "" ], + [ Report_ID => $header->{Report_ID} || "" ], + [ Release => $header->{Release} || "" ], + [ Institution_Name => $header->{Institution_Name} || "" ], + [ Institution_ID => join( "; ", map( $_->{Type} . ":" . $_->{Value}, @{ $header->{Institution_ID} } ) ) || "" ], + [ Metric_Types => join( "; ", split( /\|/, $metric_types_string[0] ) ) || "" ], + [ Report_Filters => join( "; ", map( $_->{Name} . ":" . $_->{Value}, @{ $header->{Report_Filters} } ) ) || "" ], + + #TODO: Report_Attributes may need parsing, test this with a SUSHI response that provides it + [ Report_Attributes => $header->{Report_Attributes} || "" ], + [ + Exceptions => join( + "; ", map( $_->{Code} . ": " . $_->{Message} . " (" . $_->{Data} . ")", @{ $header->{Exceptions} } ) + ) + || "" + ], + [ Reporting_Period => "Begin_Date=" . $begin_date . "; End_Date=" . $end_date ], + [ Created => $header->{Created} || "" ], + [ Created_By => $header->{Created_By} || "" ], + [""] #empty 13th line (COUNTER 5) + ); +} + +=head3 _get_SUSHI_Name_Value + +Returns "Value" of a given "Name" + +=cut + +sub _get_SUSHI_Name_Value { + my ( $self, $item, $name ) = @_; + + my @value = map( $_->{Name} eq $name ? $_->{Value} : (), @{$item} ); + return $value[0]; +} + +=head3 _get_SUSHI_Type_Value + +Returns "Value" of a given "Type" + +=cut + +sub _get_SUSHI_Type_Value { + my ( $self, $item, $type ) = @_; + + my @value = map( $_->{Type} eq $type ? $_->{Value} : (), @{$item} ); + return $value[0]; +} + +=head3 _get_COUNTER_row_usages + +Returns the total and monthly usages for a row + +=cut + +sub _get_COUNTER_row_usages { + my ( $self, $row, $metric_type ) = @_; + + my @usage_months = $self->_get_usage_months( $self->{sushi}->{header} ); + + my @usage_months_fields = (); + my $count_total = 0; + + foreach my $usage_month (@usage_months) { + my $month_is_empty = 1; + + foreach my $performance ( @{ $row->{Performance} } ) { + my $period = $performance->{Period}; + my $period_usage_month = substr( $period->{Begin_Date}, 0, 7 ); + + my $instances = $performance->{Instance}; + my @metric_type_count = + map( $_->{Metric_Type} eq $metric_type ? $_->{Count} : (), + @{$instances} ); + + if ( $period_usage_month eq $usage_month && $metric_type_count[0] ) { + push( @usage_months_fields, $metric_type_count[0] ); + $count_total += $metric_type_count[0]; + $month_is_empty = 0; + } + } + + if ($month_is_empty) { + push( @usage_months_fields, 0 ); + } + } + return ( $count_total, @usage_months_fields ); +} + +=head3 get_SUSHI_metric_types + + Returns all metric types this SUSHI result has statistics for, as an array of strings. + +=cut + +sub get_SUSHI_metric_types { + my ( $self, $report_row ) = @_; + + my @metric_types = (); + + # Grab all metric_types this SUSHI result has statistics for + foreach my $performance ( @{ $report_row->{Performance} } ) { + my @SUSHI_metric_types = + map( $_->{Metric_Type}, @{ $performance->{Instance} } ); + + foreach my $sushi_metric_type (@SUSHI_metric_types) { + push( @metric_types, $sushi_metric_type ) unless grep { $_ eq $sushi_metric_type } @metric_types; + } + } + + return @metric_types; +} + +=head3 _COUNTER_report_body + +Return the COUNTER report body as an array + +=cut + +sub _COUNTER_report_body { + my ($self) = @_; + + my $header = $self->{sushi}->{header}; + my $body = $self->{sushi}->{body}; + + my @report_body = (); + + foreach my $report_row ( @{$body} ) { + my @metric_types = $self->get_SUSHI_metric_types($report_row); + + # Add one report row for each metric_type we're working with + foreach my $metric_type (@metric_types) { + push( @report_body, $self->_COUNTER_report_row( $report_row, $metric_type ) ); + } + } + + return @report_body; +} + +=head3 _COUNTER_title_report_row + +Return a COUNTER title for the COUNTER titles report body +https://cop5.projectcounter.org/en/5.0.2/04-reports/03-title-reports.html#column-headings-elements + +=cut + +sub _COUNTER_title_report_row { + my ( $self, $title_row, $metric_type, $total_usage, $monthly_usages ) = @_; + + my $header = $self->{sushi}->{header}; + my $specific_fields = $self->get_report_type_specific_fields( $header->{Report_ID} ); + + return ( + [ + # Title + $title_row->{Title} || "", + + # Publisher + $title_row->{Publisher} || "", + + # Publisher_ID + $self->_get_SUSHI_Type_Value( $title_row->{Publisher_ID}, "ISNI" ) || "", + + # Platform + $title_row->{Platform} || "", + + # DOI + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "DOI" ) || "", + + # Proprietary_ID + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Proprietary" ) || "", + + # ISBN + grep ( /ISBN/, @{$specific_fields} ) + ? ( $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "ISBN" ) || "" ) + : (), + + # Print_ISSN + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Print_ISSN" ) || "", + + # Online_ISSN + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Online_ISSN" ) || "", + + # URI - FIXME: What goes in URI? + "", + + # YOP + grep ( /YOP/, @{$specific_fields} ) ? ( $title_row->{YOP} || "" ) : (), + + # Access_Type + grep ( /Access_Type/, @{$specific_fields} ) ? ( $title_row->{Access_Type} || "" ) : (), + + # Metric_Type + $metric_type, + + # Report_Period_Total + $total_usage, + + # Monthly usage entries + @{$monthly_usages} + ] + ); +} + +=head3 _COUNTER_titles_report_column_headings + +Return titles report column headings + +=cut + +sub _COUNTER_titles_report_column_headings { + my ($self) = @_; + + my $header = $self->{sushi}->{header}; + my @month_headings = $self->_get_usage_months( $header, 1 ); + my $specific_fields = $self->get_report_type_specific_fields( $header->{Report_ID} ); + + return ( + [ + "Title", + "Publisher", + "Publisher_ID", + "Platform", + "DOI", + "Proprietary_ID", + grep ( /ISBN/, @{$specific_fields} ) ? ("ISBN") : (), + "Print_ISSN", + "Online_ISSN", + "URI", + + #"Data_Type", #TODO: Only if requested (?) + #"Section_Type", #TODO: Only if requested (?) + grep ( /YOP/, @{$specific_fields} ) ? ("YOP") : (), + grep ( /Access_Type/, @{$specific_fields} ) ? ("Access_Type") : (), + + #"Access_Method", #TODO: Only if requested (?) + "Metric_Type", + "Reporting_Period_Total", + + # @month_headings in "Mmm-yyyy" format. TODO: Show unless Exclude_Monthly_Details=true + @month_headings + ] + ); +} + +=head3 get_report_type_specific_fields + +Returns the specific fields for a given report_type + +=cut + +sub get_report_type_specific_fields { + my ( $self, $report_type ) = @_; + + my %report_type_map = ( + "TR_B1" => [ 'YOP', 'ISBN' ], + "TR_B2" => [ 'YOP', 'ISBN' ], + "TR_B3" => [ 'YOP', 'Access_Type', 'ISBN' ], + "TR_J3" => ['Access_Type'], + "TR_J4" => ['YOP'], + "IR_A1" => [ + 'Authors', 'Publication_Date', 'Article_Version', 'Print_ISSN', 'Online_ISSN', 'Parent_Title', + 'Parent_Authors', 'Parent_Article_Version', 'Parent_DOI', 'Parent_Proprietary_ID', 'Parent_Print_ISSN', + 'Parent_Online_ISSN', 'Parent_URI', 'Access_Type' + ], + ); + + return $report_type_map{$report_type}; + +} + +1; diff --git a/Koha/ERM/EUsage/COUNTER/5_1.pm b/Koha/ERM/EUsage/COUNTER/5_1.pm new file mode 100644 index 00000000000..62e0038e124 --- /dev/null +++ b/Koha/ERM/EUsage/COUNTER/5_1.pm @@ -0,0 +1,468 @@ +package Koha::ERM::EUsage::COUNTER::5_1; + +# Copyright 2025 Open Fifth + +# 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 . + +use Modern::Perl; + +use base qw(Koha::ERM::EUsage::SushiCounter); + +=head1 NAME + +Koha::ERM::EUsage::COUNTER::5 - Koha COUNTER 5.1 Object class + +=head1 API + +=head2 Class Methods + +=head3 new + + my $sushi_counter = + Koha::ERM::EUsage::SushiCounter->new( { response => decode_json( $response->decoded_content ) } ); + +=cut + +sub new { + my ($class) = @_; + my $self = {}; + + bless( $self, $class ); +} + +=head3 _COUNTER_report_header + +Return a COUNTER report header +https://cop5.projectcounter.org/en/5.1/04-reports/03-title-reports.html + +=cut + +sub _COUNTER_report_header { + my ($self) = @_; + + my $header = $self->{sushi}->{header}; + + my $metric_types = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Metric_Type" ); + my @metric_types_array = split( /\|/, $metric_types ); + $metric_types = join( "; ", @metric_types_array ) if @metric_types_array; + + my $begin_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Begin_Date" ); + my $end_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "End_Date" ); + + return ( + [ Report_Name => $header->{Report_Name} || "" ], + [ Report_ID => $header->{Report_ID} || "" ], + [ Release => $header->{Release} || "" ], + [ Institution_Name => $header->{Institution_Name} || "" ], + [ Institution_ID => $self->get_mapped_SUSHI_values( 'Type', $header->{Institution_ID} ) || "" ], + [ Metric_Types => $metric_types || "" ], + [ Report_Filters => $self->get_mapped_SUSHI_values( 'Name', $header->{Report_Filters} ) || "" ], + + #TODO: Report_Attributes may need parsing, test this with a SUSHI response that provides it + [ Report_Attributes => $header->{Report_Attributes} || "" ], + [ + Exceptions => join( + "; ", map( $_->{Code} . ": " . $_->{Message} . " (" . $_->{Data} . ")", @{ $header->{Exceptions} } ) + ) + || "" + ], + [ Reporting_Period => "Begin_Date=" . $begin_date . "; End_Date=" . $end_date ], + [ Created => $header->{Created} || "" ], + [ Created_By => $header->{Created_By} || "" ], + [ Registry_Record => $header->{Registry_Record} || "" ], + [""] #empty 14th line (COUNTER 5.1) + ); +} + +=head3 get_counter51_value + + Given a SUSHI response, get the value for the given key in the correct format for the COUNTER report + https://counter5.cambridge.org/r51/sushi-docs/ + +=cut + +sub get_counter51_value { + my ( $self, $item, $key ) = @_; + my $counter_51_value = $item->{$key}; + if ( ref($counter_51_value) eq 'ARRAY' ) { + return join( "; ", @$counter_51_value ); + } else { + return $counter_51_value; + } +} + +=head3 get_mapped_SUSHI_values + + Given a SUSHI response, get the value for the given key in the correct format for the COUNTER report + https://counter5.cambridge.org/r51/sushi-docs/ + + It will return a string of the form "Name:Value;Name:Value;..." + If the value is an array with a single element, just the value will be returned. + If the value is an array with multiple elements, they will be joined with "|" + +=cut + +sub get_mapped_SUSHI_values { + my ( $self, $key, $sushi_type_value ) = @_; + + return join( + "; ", + map { + $_ . ":" + . ( + ref $sushi_type_value->{$_} eq 'ARRAY' + && scalar( @{ $sushi_type_value->{$_} } ) == 1 ? $sushi_type_value->{$_}[0] + : ( ref $sushi_type_value->{$_} eq 'ARRAY' && scalar( @{ $sushi_type_value->{$_} } ) > 1 ) + ? join( "|", @{ $sushi_type_value->{$_} } ) + : $sushi_type_value->{$_} + ) + } keys %{$sushi_type_value} + ); +} + +=head3 _get_SUSHI_Name_Value + +Returns "Value" of a given "Name" + +=cut + +sub _get_SUSHI_Name_Value { + my ( $self, $item, $name ) = @_; + + return $self->get_counter51_value( $item, $name ); +} + +=head3 _get_SUSHI_Type_Value + +Returns "Value" of a given "Type" + +=cut + +sub _get_SUSHI_Type_Value { + my ( $self, $item, $type ) = @_; + + return $self->get_counter51_value( $item, $type ); +} + +=head3 _get_COUNTER_row_usages + +Returns the total and monthly usages for a row + +=cut + +sub _get_COUNTER_row_usages { + my ( $self, $row, $metric_type, $access_type ) = @_; + + my @usage_months = $self->_get_usage_months( $self->{sushi}->{header} ); + + my @usage_months_fields = (); + my $count_total = 0; + + foreach my $usage_month (@usage_months) { + my $month_is_empty = 1; + + my $usage_to_add; + if ($access_type) { + my @access_type_performances = + grep { $_->{Access_Type} eq $access_type } @{ $row->{Attribute_Performance} }; + $usage_to_add = $access_type_performances[0]->{Performance}->{$metric_type}->{$usage_month}; + } else { + $usage_to_add = $row->{Attribute_Performance}->[0]->{Performance}->{$metric_type}->{$usage_month}; + } + if ( defined $usage_to_add ) { + $count_total += $usage_to_add; + $month_is_empty = 0; + push( @usage_months_fields, $usage_to_add ); + } + + if ($month_is_empty) { + push( @usage_months_fields, 0 ); + } + } + + return ( $count_total, @usage_months_fields ); +} + +=head3 get_SUSHI_metric_types + + Given a SUSHI report row, return a sorted list of all the metric types + +=cut + +sub get_SUSHI_metric_types { + my ( $self, $report_row ) = @_; + + my @metric_types = (); + @metric_types = keys %{ $report_row->{Attribute_Performance}->[0]->{Performance} }; + @metric_types = sort @metric_types; + return @metric_types; +} + +=head3 _COUNTER_report_body + +Return the COUNTER report body as an array + +=cut + +sub _COUNTER_report_body { + my ($self) = @_; + + my $header = $self->{sushi}->{header}; + my $body = $self->{sushi}->{body}; + + my @report_body = (); + + foreach my $report_row ( @{$body} ) { + my @metric_types = $self->get_SUSHI_metric_types($report_row); + + my $specific_fields = $self->get_report_type_specific_fields( $header->{Report_ID} ); + my $use_access_type = grep ( /Access_Type/, @{$specific_fields} ) ? 1 : 0; + my $use_yop = grep ( /YOP/, @{$specific_fields} ) ? 1 : 0; + my $use_data_type = grep ( /Data_Type/, @{$specific_fields} ) ? 1 : 0; + + if ($use_data_type) { + my %data_types = map { $_->{Data_Type} => 1 } @{ $report_row->{Attribute_Performance} }; + my @data_types = sort keys %data_types; + + foreach my $data_type (@data_types) { + + if ($use_yop) { + my %yops = map { $_->{YOP} => 1 } @{ $report_row->{Attribute_Performance} }; + my @yops = sort { $b <=> $a } keys %yops; + foreach my $yop (@yops) { + + if ($use_access_type) { + my @access_types = map { $_->{"Access_Type"} } @{ $report_row->{Attribute_Performance} }; + foreach my $access_type (@access_types) { + + # Add one report row for each metric_type we're working with + foreach my $metric_type (@metric_types) { + push( + @report_body, + $self->_COUNTER_report_row( + $report_row, $metric_type, $access_type, $yop, + $data_type + ) + ); + } + } + } else { + + # Add one report row for each metric_type we're working with + foreach my $metric_type (@metric_types) { + push( + @report_body, + $self->_COUNTER_report_row( $report_row, $metric_type, undef, $yop, $data_type ) + ); + } + } + } + } else { + + # Add one report row for each metric_type we're working with + foreach my $metric_type (@metric_types) { + push( + @report_body, + $self->_COUNTER_report_row( $report_row, $metric_type, undef, undef, $data_type ) + ); + } + } + } + } elsif ($use_yop) { + my %yops = map { $_->{YOP} => 1 } @{ $report_row->{Attribute_Performance} }; + my @yops = sort { $b <=> $a } keys %yops; + foreach my $yop (@yops) { + + if ($use_access_type) { + my @access_types = map { $_->{"Access_Type"} } @{ $report_row->{Attribute_Performance} }; + foreach my $access_type (@access_types) { + + # Add one report row for each metric_type we're working with + foreach my $metric_type (@metric_types) { + push( + @report_body, + $self->_COUNTER_report_row( $report_row, $metric_type, $access_type, $yop ) + ); + } + } + } else { + + # Add one report row for each metric_type we're working with + foreach my $metric_type (@metric_types) { + push( @report_body, $self->_COUNTER_report_row( $report_row, $metric_type, undef, $yop ) ); + } + } + } + + } elsif ($use_access_type) { + my @access_types = map { $_->{"Access_Type"} } @{ $report_row->{Attribute_Performance} }; + foreach my $access_type (@access_types) { + + # Add one report row for each metric_type we're working with + foreach my $metric_type (@metric_types) { + push( @report_body, $self->_COUNTER_report_row( $report_row, $metric_type, $access_type ) ); + } + } + } else { + foreach my $metric_type (@metric_types) { + push( @report_body, $self->_COUNTER_report_row( $report_row, $metric_type ) ); + } + } + + } + + return @report_body; +} + +=head3 _COUNTER_title_report_row + +Return a COUNTER title for the COUNTER titles report body +https://cop5.countermetrics.org/en/5.1.0.1/04-reports/03-title-reports.html#column-headings-elements + +=cut + +sub _COUNTER_title_report_row { + my ( $self, $title_row, $metric_type, $total_usage, $monthly_usages, $access_type, $yop, $data_type ) = @_; + + my $header = $self->{sushi}->{header}; + my $specific_fields = $self->get_report_type_specific_fields( $header->{Report_ID} ); + my $access_type_to_use = $access_type ? $access_type : ( $title_row->{Access_Type} || "" ); + my $yop_to_use = $yop ? $yop : ( $title_row->{YOP} || "" ); + my $data_type_to_use = $data_type ? $data_type : ( $title_row->{Data_Type} || "" ); + + return ( + [ + # Title + $title_row->{Title} || "", + + # Publisher + $title_row->{Publisher} || "", + + # Publisher_ID + $self->_get_SUSHI_Type_Value( $title_row->{Publisher_ID}, "ISNI" ) || "", + + # Platform + $title_row->{Platform} || "", + + # DOI + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "DOI" ) || "", + + # Proprietary_ID + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Proprietary" ) || "", + + # ISBN + grep ( /ISBN/, @{$specific_fields} ) + ? ( $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "ISBN" ) || "" ) + : (), + + # Print_ISSN + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Print_ISSN" ) || "", + + # Online_ISSN + $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Online_ISSN" ) || "", + + # URI - FIXME: What goes in URI? + "", + + # Data_Type + grep ( /Data_Type/, @{$specific_fields} ) ? $data_type_to_use : (), + + # YOP + grep ( /YOP/, @{$specific_fields} ) ? $yop_to_use : (), + + # Access_Type + grep ( /Access_Type/, @{$specific_fields} ) ? $access_type_to_use : (), + + # Metric_Type + $metric_type, + + # Report_Period_Total + $total_usage, + + # Monthly usage entries + @{$monthly_usages} + ] + ); +} + +=head3 _COUNTER_titles_report_column_headings + +Return titles report column headings + +=cut + +sub _COUNTER_titles_report_column_headings { + my ($self) = @_; + + my $header = $self->{sushi}->{header}; + my @month_headings = $self->_get_usage_months( $header, 1 ); + my $specific_fields = $self->get_report_type_specific_fields( $header->{Report_ID} ); + + return ( + [ + "Title", + "Publisher", + "Publisher_ID", + "Platform", + "DOI", + "Proprietary_ID", + grep ( /ISBN/, @{$specific_fields} ) ? ("ISBN") : (), + "Print_ISSN", + "Online_ISSN", + "URI", + grep ( /Data_Type/, @{$specific_fields} ) ? ("Data_Type") : (), + + #"Section_Type", #TODO: Only if requested (?) + grep ( /YOP/, @{$specific_fields} ) ? ("YOP") : (), + grep ( /Access_Type/, @{$specific_fields} ) ? ("Access_Type") : (), + + #"Access_Method", #TODO: Only if requested (?) + "Metric_Type", + "Reporting_Period_Total", + + # @month_headings in "Mmm-yyyy" format. TODO: Show unless Exclude_Monthly_Details=true + @month_headings + ] + ); +} + +=head3 get_report_type_specific_fields + +Returns the specific fields for a given report_type + +=cut + +sub get_report_type_specific_fields { + my ( $self, $report_type ) = @_; + + my %report_type_map = ( + "TR_B1" => [ 'YOP', 'Data_Type', 'ISBN' ], + "TR_B2" => [ 'YOP', 'Data_Type', 'ISBN' ], + "TR_B3" => [ 'YOP', 'Data_Type', 'Access_Type', 'ISBN' ], + "TR_J3" => ['Access_Type'], + "TR_J4" => ['YOP'], + "IR_A1" => [ + 'Authors', 'Publication_Date', 'Article_Version', 'Print_ISSN', 'Online_ISSN', 'Parent_Title', + 'Parent_Authors', 'Parent_Article_Version', 'Parent_DOI', 'Parent_Proprietary_ID', 'Parent_Print_ISSN', + 'Parent_Online_ISSN', 'Parent_URI', 'Access_Type' + ], + ); + + return $report_type_map{$report_type}; + +} + +1; diff --git a/Koha/ERM/EUsage/CounterFile.pm b/Koha/ERM/EUsage/CounterFile.pm index 2d5d80f15cd..18a653c2de6 100644 --- a/Koha/ERM/EUsage/CounterFile.pm +++ b/Koha/ERM/EUsage/CounterFile.pm @@ -85,6 +85,12 @@ Receive background_job_callbacks to be able to update job progress sub store { my ( $self, $background_job_callbacks ) = @_; + # DOCS: + # COUNTER 5 CSV examples: + # https://cop5.countermetrics.org/en/5.0.3/appendices/h-sample-counter-master-reports-and-standard-views.html + # COUNTER 5.1 CSV examples: + # https://cop5.projectcounter.org/en/5.1/appendices/g-sample-counter-reports-and-standard-views.html + $self->_set_report_type_from_file; my $result = $self->SUPER::store; @@ -227,7 +233,10 @@ sub _add_monthly_usage_entries { my $usage_data_provider = $self->get_usage_data_provider; my $usage_object_info = $self->_get_usage_object_id_hash($usage_object); - my $specific_fields = Koha::ERM::EUsage::SushiCounter->get_report_type_specific_fields( $self->type ); + my $specific_fields = Koha::ERM::EUsage::SushiCounter->get_report_type_specific_fields_by_release( + $self->type, + $self->get_COUNTER_report_release->{header_value} + ); $usage_object->monthly_usages( [ @@ -262,7 +271,10 @@ sub _add_yearly_usage_entries { my $usage_data_provider = $self->get_usage_data_provider; my $usage_object_info = $self->_get_usage_object_id_hash($usage_object); - my $specific_fields = Koha::ERM::EUsage::SushiCounter->get_report_type_specific_fields( $self->type ); + my $specific_fields = Koha::ERM::EUsage::SushiCounter->get_report_type_specific_fields_by_release( + $self->type, + $self->get_COUNTER_report_release->{header_value} + ); while ( my ( $year, $usage ) = each( %{$yearly_usages} ) ) { @@ -303,21 +315,12 @@ A I exception is thrown sub validate { my ($self) = @_; - open my $fh, "<", \$self->file_content or die; - my $csv = Text::CSV_XS->new( { binary => 1, always_quote => 1, eol => $/, decode_utf8 => 1 } ); - - $csv->column_names(qw( header_key header_value )); - my @header_rows = $csv->getline_hr_all( $fh, 0, 12 ); - my @header = $header_rows[0]; - - my @release_row = - map( $_->{header_key} eq 'Release' ? $_ : (), @{ $header[0] } ); - my $release = $release_row[0]; + my $release = $self->get_COUNTER_report_release; # TODO: Validate that there is an empty row between header and body Koha::Exceptions::ERM::EUsage::CounterFile::UnsupportedRelease->throw - if $release && $release->{header_value} != 5; + if $release && ( $release->{header_value} != 5 && $release->{header_value} ne '5.1' ); } @@ -344,6 +347,27 @@ sub _set_report_type_from_file { $self->type( $report->{header_value} ); } +=head3 get_COUNTER_report_release + +Retrieves the COUNTER report release for the given file_content + +=cut + +sub get_COUNTER_report_release { + my ($self) = @_; + + open my $fh, "<", \$self->file_content or die; + my $csv = Text::CSV_XS->new( { binary => 1, always_quote => 1, eol => $/, decode_utf8 => 1 } ); + + $csv->column_names(qw( header_key header_value )); + my @header_rows = $csv->getline_hr_all( $fh, 0, 12 ); + my @header = $header_rows[0]; + + my @release_row = + map( $_->{header_key} eq 'Release' ? $_ : (), @{ $header[0] } ); + return $release_row[0]; +} + =head3 _get_rows_from_COUNTER_file Returns array of rows from COUNTER file @@ -356,7 +380,12 @@ sub _get_rows_from_COUNTER_file { open my $fh, "<", \$self->file_content or die; my $csv = Text::CSV_XS->new( { binary => 1, always_quote => 1, eol => $/, decode_utf8 => 1 } ); - my $header_columns = $csv->getline_all( $fh, 13, 1 ); + my $release = $self->get_COUNTER_report_release; + $release = $release->{header_value}; + + my $header_rows = $release eq '5.1' ? 14 : 13; + + my $header_columns = $csv->getline_all( $fh, $header_rows, 1 ); $csv->column_names( @{$header_columns}[0] ); # Get all rows from 14th onward @@ -588,7 +617,10 @@ sub _add_usage_object_entry { my ( $self, $row ) = @_; my $usage_data_provider = $self->get_usage_data_provider; - my $specific_fields = Koha::ERM::EUsage::SushiCounter->get_report_type_specific_fields( $self->type ); + my $specific_fields = Koha::ERM::EUsage::SushiCounter->get_report_type_specific_fields_by_release( + $self->type, + $self->get_COUNTER_report_release->{header_value} + ); if ( $self->type =~ /PR/i ) { my $new_usage_platform = Koha::ERM::EUsage::UsagePlatform->new( diff --git a/Koha/ERM/EUsage/SushiCounter.pm b/Koha/ERM/EUsage/SushiCounter.pm index 10bde4ea210..b87f2c92122 100644 --- a/Koha/ERM/EUsage/SushiCounter.pm +++ b/Koha/ERM/EUsage/SushiCounter.pm @@ -41,15 +41,26 @@ Koha::ERM::EUsage::SushiCounter - Koha SushiCounter Object class sub new { my ( $class, $params ) = @_; - my $self = $class->SUPER::new; - $self->{sushi} = { + my $counter_release; + if ( $params->{response}->{Report_Header}->{Release} eq "5" ) { + require Koha::ERM::EUsage::COUNTER::5; + $counter_release = Koha::ERM::EUsage::COUNTER::5->new; + } elsif ( $params->{response}->{Report_Header}->{Release} eq "5.1" ) { + require Koha::ERM::EUsage::COUNTER::5_1; + $counter_release = Koha::ERM::EUsage::COUNTER::5_1->new; + } else { + Koha::Exceptions::ERM::EUsage::CounterFile::UnsupportedRelease->throw( + { counter_release => $params->{response}->{Report_Header}->{Release} } ); + } + + $counter_release->{sushi} = { header => $params->{response}->{Report_Header}, body => $params->{response}->{Report_Items} }; #TODO: Handle empty $self->{sushi}->{body} here! - return $self; + return $counter_release; } =head3 get_COUNTER_from_SUSHI @@ -92,47 +103,6 @@ sub _build_COUNTER_report_file { } -=head3 _COUNTER_report_header - -Return a COUNTER report header -https://cop5.projectcounter.org/en/5.0.2/04-reports/03-title-reports.html - -=cut - -sub _COUNTER_report_header { - my ($self) = @_; - - my $header = $self->{sushi}->{header}; - - my @metric_types_string = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Metric_Type" ); - - my $begin_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Begin_Date" ); - my $end_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "End_Date" ); - - return ( - [ Report_Name => $header->{Report_Name} || "" ], - [ Report_ID => $header->{Report_ID} || "" ], - [ Release => $header->{Release} || "" ], - [ Institution_Name => $header->{Institution_Name} || "" ], - [ Institution_ID => join( "; ", map( $_->{Type} . ":" . $_->{Value}, @{ $header->{Institution_ID} } ) ) || "" ], - [ Metric_Types => join( "; ", split( /\|/, $metric_types_string[0] ) ) || "" ], - [ Report_Filters => join( "; ", map( $_->{Name} . ":" . $_->{Value}, @{ $header->{Report_Filters} } ) ) || "" ], - - #TODO: Report_Attributes may need parsing, test this with a SUSHI response that provides it - [ Report_Attributes => $header->{Report_Attributes} || "" ], - [ - Exceptions => join( - "; ", map( $_->{Code} . ": " . $_->{Message} . " (" . $_->{Data} . ")", @{ $header->{Exceptions} } ) - ) - || "" - ], - [ Reporting_Period => "Begin_Date=" . $begin_date . "; End_Date=" . $end_date ], - [ Created => $header->{Created} || "" ], - [ Created_By => $header->{Created_By} || "" ], - [""] #empty 13th line (COUNTER 5) - ); -} - =head3 _COUNTER_item_report_row Return a COUNTER item for the COUNTER items report body @@ -274,71 +244,6 @@ sub _COUNTER_platform_report_row { ); } -=head3 _COUNTER_title_report_row - -Return a COUNTER title for the COUNTER titles report body -https://cop5.projectcounter.org/en/5.0.2/04-reports/03-title-reports.html#column-headings-elements - -=cut - -sub _COUNTER_title_report_row { - my ( $self, $title_row, $metric_type, $total_usage, $monthly_usages ) = @_; - - my $header = $self->{sushi}->{header}; - my $specific_fields = $self->get_report_type_specific_fields( $header->{Report_ID} ); - - return ( - [ - # Title - $title_row->{Title} || "", - - # Publisher - $title_row->{Publisher} || "", - - # Publisher_ID - $self->_get_SUSHI_Type_Value( $title_row->{Publisher_ID}, "ISNI" ) || "", - - # Platform - $title_row->{Platform} || "", - - # DOI - $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "DOI" ) || "", - - # Proprietary_ID - $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Proprietary" ) || "", - - # ISBN - grep ( /ISBN/, @{$specific_fields} ) - ? ( $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "ISBN" ) || "" ) - : (), - - # Print_ISSN - $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Print_ISSN" ) || "", - - # Online_ISSN - $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Online_ISSN" ) || "", - - # URI - FIXME: What goes in URI? - "", - - # YOP - grep ( /YOP/, @{$specific_fields} ) ? ( $title_row->{YOP} || "" ) : (), - - # Access_Type - grep ( /Access_Type/, @{$specific_fields} ) ? ( $title_row->{Access_Type} || "" ) : (), - - # Metric_Type - $metric_type, - - # Report_Period_Total - $total_usage, - - # Monthly usage entries - @{$monthly_usages} - ] - ); -} - =head3 _COUNTER_report_row Return a COUNTER row for the COUNTER report body @@ -346,11 +251,11 @@ Return a COUNTER row for the COUNTER report body =cut sub _COUNTER_report_row { - my ( $self, $report_row, $metric_type ) = @_; + my ( $self, $report_row, $metric_type, $access_type, $yop, $data_type ) = @_; my $header = $self->{sushi}->{header}; - my ( $total_usage, @monthly_usages ) = $self->_get_COUNTER_row_usages( $report_row, $metric_type ); + my ( $total_usage, @monthly_usages ) = $self->_get_COUNTER_row_usages( $report_row, $metric_type, $access_type ); if ( $header->{Report_ID} =~ /PR/i ) { return $self->_COUNTER_platform_report_row( @@ -360,115 +265,15 @@ sub _COUNTER_report_row { } elsif ( $header->{Report_ID} =~ /DR/i ) { return $self->_COUNTER_database_report_row( $report_row, $metric_type, $total_usage, \@monthly_usages ); } elsif ( $header->{Report_ID} =~ /IR/i ) { + + #TODO: Send $access_type to _COUNTER_item_report_row like we're doing with _COUNTER_title_report_row return $self->_COUNTER_item_report_row( $report_row, $metric_type, $total_usage, \@monthly_usages ); } elsif ( $header->{Report_ID} =~ /TR/i ) { - return $self->_COUNTER_title_report_row( $report_row, $metric_type, $total_usage, \@monthly_usages ); - } -} - -=head3 _get_COUNTER_row_usages - -Returns the total and monthly usages for a row - -=cut - -sub _get_COUNTER_row_usages { - my ( $self, $row, $metric_type ) = @_; - - my @usage_months = $self->_get_usage_months( $self->{sushi}->{header} ); - - my @usage_months_fields = (); - my $count_total = 0; - - foreach my $usage_month (@usage_months) { - my $month_is_empty = 1; - - foreach my $performance ( @{ $row->{Performance} } ) { - my $period = $performance->{Period}; - my $period_usage_month = substr( $period->{Begin_Date}, 0, 7 ); - - my $instances = $performance->{Instance}; - my @metric_type_count = - map( $_->{Metric_Type} eq $metric_type ? $_->{Count} : (), - @{$instances} ); - - if ( $period_usage_month eq $usage_month && $metric_type_count[0] ) { - push( @usage_months_fields, $metric_type_count[0] ); - $count_total += $metric_type_count[0]; - $month_is_empty = 0; - } - } - - if ($month_is_empty) { - push( @usage_months_fields, 0 ); - } - } - return ( $count_total, @usage_months_fields ); -} - -=head3 _COUNTER_report_body - -Return the COUNTER report body as an array - -=cut - -sub _COUNTER_report_body { - my ($self) = @_; - - my $header = $self->{sushi}->{header}; - my $body = $self->{sushi}->{body}; - - my @report_body = (); - - foreach my $report_row ( @{$body} ) { - - my @metric_types = (); - - # Grab all metric_types this SUSHI result has statistics for - foreach my $performance ( @{ $report_row->{Performance} } ) { - my @SUSHI_metric_types = - map( $_->{Metric_Type}, @{ $performance->{Instance} } ); - - foreach my $sushi_metric_type (@SUSHI_metric_types) { - push( @metric_types, $sushi_metric_type ) unless grep { $_ eq $sushi_metric_type } @metric_types; - } - } - - # Add one report row for each metric_type we're working with - foreach my $metric_type (@metric_types) { - push( @report_body, $self->_COUNTER_report_row( $report_row, $metric_type ) ); - } + return $self->_COUNTER_title_report_row( + $report_row, $metric_type, $total_usage, \@monthly_usages, + $access_type, $yop, $data_type + ); } - - return @report_body; -} - -=head3 _get_SUSHI_Name_Value - -Returns "Value" of a given "Name" - -=cut - -sub _get_SUSHI_Name_Value { - my ( $self, $item, $name ) = @_; - - my @value = map( $_->{Name} eq $name ? $_->{Value} : (), @{$item} ); - - return $value[0]; -} - -=head3 _get_SUSHI_Type_Value - -Returns "Value" of a given "Type" - -=cut - -sub _get_SUSHI_Type_Value { - my ( $self, $item, $type ) = @_; - - my @value = map( $_->{Type} eq $type ? $_->{Value} : (), @{$item} ); - - return $value[0]; } =head3 _COUNTER_report_column_headings @@ -617,45 +422,22 @@ sub _COUNTER_platforms_report_column_headings { ); } -=head3 _COUNTER_titles_report_column_headings +=head3 get_report_type_specific_fields_by_release -Return titles report column headings +Returns the specific fields for a given report_type =cut -sub _COUNTER_titles_report_column_headings { - my ($self) = @_; - - my $header = $self->{sushi}->{header}; - my @month_headings = $self->_get_usage_months( $header, 1 ); - my $specific_fields = $self->get_report_type_specific_fields( $header->{Report_ID} ); - - return ( - [ - "Title", - "Publisher", - "Publisher_ID", - "Platform", - "DOI", - "Proprietary_ID", - grep ( /ISBN/, @{$specific_fields} ) ? ("ISBN") : (), - "Print_ISSN", - "Online_ISSN", - "URI", - - #"Data_Type", #TODO: Only if requested (?) - #"Section_Type", #TODO: Only if requested (?) - grep ( /YOP/, @{$specific_fields} ) ? ("YOP") : (), - grep ( /Access_Type/, @{$specific_fields} ) ? ("Access_Type") : (), +sub get_report_type_specific_fields_by_release { + my ( $self, $report_type, $counter_release ) = @_; - #"Access_Method", #TODO: Only if requested (?) - "Metric_Type", - "Reporting_Period_Total", - - # @month_headings in "Mmm-yyyy" format. TODO: Show unless Exclude_Monthly_Details=true - @month_headings - ] - ); + if ( $counter_release eq "5" ) { + require Koha::ERM::EUsage::COUNTER::5; + return Koha::ERM::EUsage::COUNTER::5->get_report_type_specific_fields($report_type); + } elsif ( $counter_release eq "5.1" ) { + require Koha::ERM::EUsage::COUNTER::5_1; + return Koha::ERM::EUsage::COUNTER::5_1->get_report_type_specific_fields($report_type); + } } =head3 _get_usage_months @@ -672,11 +454,11 @@ sub _get_usage_months { "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ); - my @begin_date = map( $_->{Name} eq "Begin_Date" ? $_->{Value} : (), @{ $header->{Report_Filters} } ); + my @begin_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, 'Begin_Date' ); my $begin_month = substr( $begin_date[0], 5, 2 ); my $begin_year = substr( $begin_date[0], 0, 4 ); - my @end_date = map( $_->{Name} eq "End_Date" ? $_->{Value} : (), @{ $header->{Report_Filters} } ); + my @end_date = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, 'End_Date' ); my $end_month = substr( $end_date[0], 5, 2 ); my $end_year = substr( $end_date[0], 0, 4 ); @@ -699,32 +481,6 @@ sub _get_usage_months { return @month_headings; } -=head3 get_report_type_specific_fields - -Returns the specific fields for a given report_type - -=cut - -sub get_report_type_specific_fields { - my ( $self, $report_type ) = @_; - - my %report_type_map = ( - "TR_B1" => [ 'YOP', 'ISBN' ], - "TR_B2" => [ 'YOP', 'ISBN' ], - "TR_B3" => [ 'YOP', 'Access_Type', 'ISBN' ], - "TR_J3" => ['Access_Type'], - "TR_J4" => ['YOP'], - "IR_A1" => [ - 'Authors', 'Publication_Date', 'Article_Version', 'Print_ISSN', 'Online_ISSN', 'Parent_Title', - 'Parent_Authors', 'Parent_Article_Version', 'Parent_DOI', 'Parent_Proprietary_ID', 'Parent_Print_ISSN', - 'Parent_Online_ISSN', 'Parent_URI', 'Access_Type' - ], - ); - - return $report_type_map{$report_type}; - -} - =head3 _type =cut diff --git a/Koha/ERM/EUsage/UsageDataProvider.pm b/Koha/ERM/EUsage/UsageDataProvider.pm index b7acc76ab53..9f7fb5c6c76 100644 --- a/Koha/ERM/EUsage/UsageDataProvider.pm +++ b/Koha/ERM/EUsage/UsageDataProvider.pm @@ -25,6 +25,8 @@ use LWP::UserAgent; use Koha::Exceptions; +use Try::Tiny qw( catch try ); + use base qw(Koha::Object); use Koha; @@ -218,24 +220,34 @@ sub harvest_sushi { return if $self->_sushi_errors($result); # Parse the SUSHI response - my $sushi_counter = Koha::ERM::EUsage::SushiCounter->new( { response => $result } ); - my $counter_file = $sushi_counter->get_COUNTER_from_SUSHI; - - return if $self->_counter_file_size_too_large($counter_file); + try { + my $sushi_counter = Koha::ERM::EUsage::SushiCounter->new( { response => $result } ); + my $counter_file = $sushi_counter->get_COUNTER_from_SUSHI; - $self->counter_files( - [ - { - usage_data_provider_id => $self->erm_usage_data_provider_id, - file_content => $counter_file, - date_uploaded => POSIX::strftime( "%Y%m%d%H%M%S", localtime ), + return if $self->_counter_file_size_too_large($counter_file); - #TODO: add ".csv" to end of filename here - filename => $self->name . "_" . $self->{report_type}, - } - ] - ); + $self->counter_files( + [ + { + usage_data_provider_id => $self->erm_usage_data_provider_id, + file_content => $counter_file, + date_uploaded => POSIX::strftime( "%Y%m%d%H%M%S", localtime ), + #TODO: add ".csv" to end of filename here + filename => $self->name . "_" . $self->{report_type}, + } + ] + ); + } catch { + if ( $_->isa('Koha::Exceptions::ERM::EUsage::CounterFile::UnsupportedRelease') ) { + $self->{job_callbacks}->{add_message_callback}->( + { + type => 'error', + message => 'COUNTER release ' . $_->{message}->{counter_release} . ' not supported', + } + ) if $self->{job_callbacks}; + } + }; } =head3 set_background_job_callbacks @@ -269,7 +281,7 @@ Tests the connection of the harvester to the SUSHI service and returns any alert sub test_connection { my ($self) = @_; - my $url = _validate_url( $self->service_url, 'status' ); + my $url = $self->_validate_url( $self->service_url, 'status' ); $url .= 'status'; $url .= '?customer_id=' . $self->customer_id; $url .= '&requestor_id=' . $self->requestor_id if $self->requestor_id; @@ -384,7 +396,7 @@ sub _build_url_query { $self->erm_usage_data_provider_id; } - my $url = _validate_url( $self->service_url, 'harvest' ); + my $url = $self->_validate_url( $self->service_url, 'harvest' ); $url .= lc $self->{report_type}; $url .= '?customer_id=' . $self->customer_id; @@ -401,22 +413,27 @@ sub _build_url_query { Checks whether the url ends in a trailing "/" and adds one if not -my $url = _validate_url($url, 'harvest') +my $url = $self->_validate_url($url, 'harvest') $caller is either the harvest_sushi function ("harvest") or the test_connection function ("status") =cut sub _validate_url { - my ( $url, $caller ) = @_; + my ( $self, $url, $caller ) = @_; if ( $caller eq 'harvest' ) { - # Not all urls will end in "/" - add one so they are standardised $url = _check_trailing_character($url); - # All SUSHI report requests should be to the "/reports" endpoint - # Not all providers in the counter registry include this in their data so we need to check and add it + # Default to 5.1 if anything other than '5' + my $report_release = $self->report_release eq '5' ? $self->report_release : '5.1'; + + if ( $report_release eq '5.1' ) { + my $reports_param = substr $url, -4; + $url .= 'r51/' if $reports_param ne 'r51/'; + } + my $reports_param = substr $url, -8; $url .= 'reports/' if $reports_param ne 'reports/'; } else { diff --git a/Koha/Exceptions/ERM/EUsage/CounterFile.pm b/Koha/Exceptions/ERM/EUsage/CounterFile.pm index 70b00998187..db6eda07b43 100644 --- a/Koha/Exceptions/ERM/EUsage/CounterFile.pm +++ b/Koha/Exceptions/ERM/EUsage/CounterFile.pm @@ -26,7 +26,8 @@ use Exception::Class ( }, 'Koha::Exceptions::ERM::EUsage::CounterFile::UnsupportedRelease' => { isa => 'Koha::Exceptions::ERM::EUsage::CounterFile', - description => 'This COUNTER release is not supported' + description => 'This COUNTER release is not supported', + fields => ['counter_release'], } ); diff --git a/Koha/REST/V1/ERM/EUsage/CounterRegistry.pm b/Koha/REST/V1/ERM/EUsage/CounterRegistry.pm index 3804b79749e..8994de10f5a 100644 --- a/Koha/REST/V1/ERM/EUsage/CounterRegistry.pm +++ b/Koha/REST/V1/ERM/EUsage/CounterRegistry.pm @@ -90,7 +90,15 @@ sub list { my @counter_5_supporting_platforms; foreach my $platform (@$result) { my $name_check = index( lc $platform->{name}, lc $search_string ); - my @services = grep { $_->{counter_release} eq '5' } @{ $platform->{sushi_services} }; + + # TODO: + # We should also consider COUNTER 5.1 here, but the COUNTER registry response is not great. + # Visit the following URL: + # /api/v1/erm/counter_registry?_per_page=-1&q={"name":"wiley"} + # Notice the response comes back as the only sushi service being counter release 5, + # but for all supported reports, both '5' and '5.1' are returned. + + my @services = grep { $_->{counter_release} eq '5' } @{ $platform->{sushi_services} }; if ( scalar(@services) > 0 && $name_check != -1 && scalar( @{ $platform->{reports} } ) > 0 ) diff --git a/t/db_dependent/Koha/BackgroundJobs/ErmSushiHarvester.t b/t/db_dependent/Koha/BackgroundJobs/ErmSushiHarvester.t index f4f0fe2843a..fea9ebd9c00 100755 --- a/t/db_dependent/Koha/BackgroundJobs/ErmSushiHarvester.t +++ b/t/db_dependent/Koha/BackgroundJobs/ErmSushiHarvester.t @@ -17,7 +17,7 @@ use Modern::Perl; -use Test::More tests => 3; +use Test::More tests => 5; use Koha::Database; use Koha::BackgroundJobs; @@ -36,7 +36,7 @@ my $builder = t::lib::TestBuilder->new; my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); -my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../data/erm/eusage/TR_J1.json"; +my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../data/erm/eusage/COUNTER_5/TR_J1.json"; my $sushi_counter_5_response_TR_J1 = read_file($sushi_response_file_TR_J1); my $sushi_counter_report_TR_J1 = Koha::ERM::EUsage::SushiCounter->new( { response => decode_json($sushi_counter_5_response_TR_J1) } ); @@ -305,3 +305,19 @@ subtest 'enqueue_sushi_harvest_jobs_error_handling' => sub { $schema->storage->txn_rollback; }; + +my $unsupported_sushi_response_file = + dirname(__FILE__) . "/../../data/erm/eusage/UnsupportedCOUNTER/UnsupportedCOUNTER.json"; +my $unsupported_sushi_counter_5_response_TR_J1 = read_file($unsupported_sushi_response_file); + +eval { + Koha::ERM::EUsage::SushiCounter->new( { response => decode_json($unsupported_sushi_counter_5_response_TR_J1) } ); +}; +if ($@) { + isa_ok( + $@, 'Koha::Exceptions::ERM::EUsage::CounterFile::UnsupportedRelease', + 'Unsupported release throws UnsupportedRelease exception' + ); + + is( $@->{message}->{counter_release}, 99, 'exception message has counter_release set to 99' ); +} diff --git a/t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/CounterFile.t b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/CounterFile.t new file mode 100755 index 00000000000..b7e8ccb1b01 --- /dev/null +++ b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/CounterFile.t @@ -0,0 +1,133 @@ +#!/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 . + +use Modern::Perl; + +use Test::NoWarnings; +use Test::More tests => 3; + +use Koha::Database; +use Koha::ERM::EUsage::CounterFile; + +use JSON qw( decode_json ); +use File::Basename qw( dirname ); +use File::Slurp; + +use t::lib::Mocks; +use t::lib::TestBuilder; +use Test::MockModule; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); + +my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../../../data/erm/eusage/COUNTER_5.1/TR_J1.json"; +my $sushi_counter_51_response_TR_J1 = read_file($sushi_response_file_TR_J1); +my $sushi_counter_report_TR_J1 = + Koha::ERM::EUsage::SushiCounter->new( { response => decode_json($sushi_counter_51_response_TR_J1) } ); + +subtest 'store' => sub { + + plan tests => 3; + + $schema->storage->txn_begin; + + my $patron = Koha::Patrons->search()->last; + t::lib::Mocks::mock_userenv( { number => $patron->borrowernumber } ); # Is superlibrarian + + my $usage_data_provider = $builder->build_object( + { class => 'Koha::ERM::EUsage::UsageDataProviders', value => { name => 'TestProvider' } } ); + $usage_data_provider->{report_type} = 'TR_J1'; + + my $now_time = POSIX::strftime( "%Y%m%d%H%M%S", localtime ); + + my $counter_file = Koha::ERM::EUsage::CounterFile->new( + { + usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id, + file_content => $sushi_counter_report_TR_J1->get_COUNTER_from_SUSHI, + date_uploaded => $now_time, + filename => $usage_data_provider->name . "_" . $usage_data_provider->{report_type}, + } + )->store; + + # UsageTitles are added on CounterFile->store + my $titles_rs = Koha::ERM::EUsage::UsageTitles->search( + { usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id } ); + + # MonthlyUsages are added on CounterFile->store + my $mus_rs = Koha::ERM::EUsage::MonthlyUsages->search( + { usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id } ); + + # YearlyUsages are added on CounterFile->store + my $yus_rs = Koha::ERM::EUsage::YearlyUsages->search( + { usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id } ); + + is( $titles_rs->count, 2, '2 titles were added' ); + is( $mus_rs->count, 10, '10 monthly usages were added' ); + is( $yus_rs->count, 4, '4 yearly usages were added' ); + + $schema->storage->txn_rollback; + + #TODO: Test yop + #TODO: acccess_type + +}; + +subtest '_get_rows_from_COUNTER_file' => sub { + + plan tests => 4; + + $schema->storage->txn_begin; + + my $usage_data_provider = $builder->build_object( + { class => 'Koha::ERM::EUsage::UsageDataProviders', value => { name => 'TestProvider' } } ); + $usage_data_provider->{report_type} = 'TR_J1'; + + my $now_time = POSIX::strftime( "%Y%m%d%H%M%S", localtime ); + + my $counter_file = $builder->build_object( + { + class => 'Koha::ERM::EUsage::CounterFiles', + value => { + usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id, + file_content => $sushi_counter_report_TR_J1->get_COUNTER_from_SUSHI, + date_uploaded => $now_time, + filename => $usage_data_provider->name . "_" . $usage_data_provider->{report_type}, + } + } + ); + + my $counter_file_rows = $counter_file->_get_rows_from_COUNTER_file; + + is( ref $counter_file_rows, 'ARRAY', '_get_rows_from_COUNTER_file returns array' ); + is( + scalar @{$counter_file_rows}, 4, + '_get_rows_from_COUNTER_file returns correct number of rows' + ); + is( + @{$counter_file_rows}[0]->{Title}, 'Education and Training', + 'first and second report row is about the same title' + ); + is( + @{$counter_file_rows}[1]->{Title}, 'Education and Training', + 'first and second report row is about the same title' + ); + + $schema->storage->txn_rollback; + +}; diff --git a/t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/SushiCounter.t b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/SushiCounter.t new file mode 100755 index 00000000000..cdfa7d1cf57 --- /dev/null +++ b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5.1/SushiCounter.t @@ -0,0 +1,1031 @@ +#!/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 . + +use Modern::Perl; + +use Test::NoWarnings; +use Test::More tests => 19; + +use Koha::ERM::EUsage::SushiCounter; +use Koha::Database; +use JSON qw( decode_json ); +use File::Basename qw( dirname ); +use File::Slurp; + +use t::lib::TestBuilder; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +my $test_data_base_dir = "/../../../../data/erm/eusage/COUNTER_5.1"; +my $sushi_response_file_TR_J1 = dirname(__FILE__) . $test_data_base_dir . "/TR_J1.json"; +my $sushi_counter_51_response_TR_J1 = decode_json( read_file($sushi_response_file_TR_J1) ); +my $sushi_counter_TR_J1 = + Koha::ERM::EUsage::SushiCounter->new( { version => '5.1', response => $sushi_counter_51_response_TR_J1 } ); + +subtest 'TR_J1 _COUNTER_report_header' => sub { + + plan tests => 43; + + my @report_header = $sushi_counter_TR_J1->_COUNTER_report_header; + + # Header row #1 - Report_Name + is( $report_header[0][0], 'Report_Name', '1st row is report name' ); + is( $report_header[0][1], 'Journal Requests (Excluding OA_Gold)', '1st row is report name' ); + is( $report_header[0][2], undef, '1st row is report name' ); + + # Header row #2 - Report_ID + is( $report_header[1][0], 'Report_ID', '2nd row is report name' ); + is( $report_header[1][1], 'TR_J1', '2nd row is report name' ); + is( $report_header[1][2], undef, '2nd row is report name' ); + + # Header row #3 - Release + is( $report_header[2][0], 'Release', '3rd row is counter release' ); + is( $report_header[2][1], '5.1', '3rd row is counter release' ); + is( $report_header[2][2], undef, '3rd row is counter release' ); + + # Header row #4 - Institution_Name + is( $report_header[3][0], 'Institution_Name', '4th row is institution name' ); + is( $report_header[3][1], 'Test Institution', '4th row is institution name' ); + is( $report_header[3][2], undef, '4th row is institution name' ); + + # Header row #5 - Institution_ID + is( $report_header[4][0], 'Institution_ID', '5th row is institution id' ); + like( $report_header[4][1], qr/Proprietary:TInsti:EALTEST001|ISNI:0000000123456789/, '5th row is institution id' ); + is( $report_header[4][2], undef, '5th row is institution id' ); + + # Header row #6 - Metric_Types + is( $report_header[5][0], 'Metric_Types', '6th row is metric types' ); + is( $report_header[5][1], 'Total_Item_Requests; Unique_Item_Requests', '6th row is metric types' ); + is( $report_header[5][2], undef, '6th row is metric types' ); + + # Header row #7 - Report_Filters + is( $report_header[6][0], 'Report_Filters', '7th row is report filters' ); + like( $report_header[6][1], qr/End_Date:2022-09-30/, '7th row is report filters' ); + like( $report_header[6][1], qr/Access_Method:Regular/, '7th row is report filters' ); + like( + $report_header[6][1], qr/Metric_Type:Total_Item_Requests\|Unique_Item_Requests/, + '7th row is report filters' + ); + like( $report_header[6][1], qr/Data_Type:Journal/, '7th row is report filters' ); + like( $report_header[6][1], qr/Begin_Date:2021-09-01/, '7th row is report filters' ); + like( $report_header[6][1], qr/Access_Type:Controlled/, '7th row is report filters' ); + is( $report_header[6][2], undef, '7th row is report filters' ); + + # Header row #8 - Report_Attributes + is( $report_header[7][0], 'Report_Attributes', '8th row is report attributes' ); + is( $report_header[7][1], '', '8th row is report attributes' ); + is( $report_header[7][2], undef, '8th row is report attributes' ); + + # Header row #9 - Exceptions + is( $report_header[8][0], 'Exceptions', '9th row is exceptions' ); + is( $report_header[8][1], '', '9th row is exceptions' ); + is( $report_header[8][2], undef, '9th row is exceptions' ); + + # Header row #10 - Reporting_Period + is( $report_header[9][0], 'Reporting_Period', '10th row is reporting period' ); + is( $report_header[9][1], 'Begin_Date=2021-09-01; End_Date=2022-09-30', '10th row is reporting period' ); + is( $report_header[9][2], undef, '10th row is reporting period' ); + + # Header row #11 - Created + is( $report_header[10][0], 'Created', '11th row is created' ); + is( $report_header[10][1], '2023-08-29T07:11:41Z', '11th row is created' ); + is( $report_header[10][2], undef, '11th row is created' ); + + # Header row #12 - Created + is( $report_header[11][0], 'Created_By', '12th row is created by' ); + is( $report_header[11][1], 'Test Systems Inc.', '12th row is created by' ); + is( $report_header[11][2], undef, '12th row is created by' ); + + # Header row #13 - Registry_Record + is( $report_header[12][0], 'Registry_Record', '13th row is empty' ); + + # Header row #14 - This needs to be empty + is( $report_header[13][0], '', '14th row is empty' ); +}; + +subtest 'TR_J1 _COUNTER_report_column_headings' => sub { + + plan tests => 19; + + my @report_column_headings = $sushi_counter_TR_J1->_COUNTER_report_column_headings; + + # Standard TR_J1 column headings + is( $report_column_headings[0][0], 'Title', '1st column heading is title' ); + is( $report_column_headings[0][1], 'Publisher', '2nd column heading is publisher' ); + is( $report_column_headings[0][2], 'Publisher_ID', '3rd column heading is publisher ID' ); + is( $report_column_headings[0][3], 'Platform', '4th column heading is platform' ); + is( $report_column_headings[0][4], 'DOI', '5th column heading is DOI' ); + is( $report_column_headings[0][5], 'Proprietary_ID', '6th column heading is proprietary ID' ); + is( $report_column_headings[0][6], 'Print_ISSN', '7th column heading is print ISSN' ); + is( $report_column_headings[0][7], 'Online_ISSN', '8th column heading is online ISSN' ); + is( $report_column_headings[0][8], 'URI', '9th column heading is URI' ); + is( $report_column_headings[0][9], 'Metric_Type', '10th column heading is metric type' ); + is( $report_column_headings[0][10], 'Reporting_Period_Total', '11th column heading is reporting period total' ); + + # Months column headings + is( $report_column_headings[0][11], 'Sep 2021', '12th column is month column heading' ); + is( $report_column_headings[0][12], 'Oct 2021', '13th column is month column heading' ); + is( $report_column_headings[0][13], 'Nov 2021', '14th column is month column heading' ); + is( $report_column_headings[0][14], 'Dec 2021', '15th column is month column heading' ); + is( $report_column_headings[0][15], 'Jan 2022', '16th column is month column heading' ); + is( $report_column_headings[0][16], 'Feb 2022', '17th column is month column heading' ); + + # ... period is september 2021 to september 2022, i.e. 13 months + is( $report_column_headings[0][23], 'Sep 2022', '23rd column is the last month column heading' ); + is( $report_column_headings[0][24], undef, '24th column is empty, no more months' ); +}; + +subtest 'TR_J1 _COUNTER_report_body' => sub { + + plan tests => 16; + + my @report_body = $sushi_counter_TR_J1->_COUNTER_report_body; + + # The same title is sequential but for different metric types + is( $report_body[0][0], 'Education and Training', 'same title, different metric type' ); + is( $report_body[1][0], 'Education and Training', 'same title, different metric type' ); + is( $report_body[0][9], 'Total_Item_Requests', 'same title, different metric type' ); + is( $report_body[1][9], 'Unique_Item_Requests', 'same title, different metric type' ); + + # The data is in the correct column + is( $report_body[2][0], 'Test Journal', '1st column is title' ); + is( $report_body[2][1], 'Test Publisher', '2nd column is publisher' ); + is( $report_body[2][2], '0000000123456789', '3rd column heading is publisher ID' ); + is( $report_body[2][3], 'Unit Test Library', '4th column is platform' ); + is( $report_body[2][4], '10.1002/(ISSN)1111-2222', '5th column is DOI' ); + is( $report_body[2][5], 'TInsti:ABC1', '6th column is proprietary ID' ); + is( $report_body[2][6], '7777-8888', '7th column is print ISSN' ); + is( $report_body[2][7], '5555-6666', '8th column is online ISSN' ); + is( $report_body[2][8], '', '9th column is URI' ); + is( $report_body[2][9], 'Total_Item_Requests', '10th column is metric type' ); + is( $report_body[2][10], 2, '11th column is reporting period total' ); + + # The period total is the sum of all the month columns + my $stats_total = 0; + for ( my $i = 11 ; $i < 24 ; $i++ ) { + $stats_total += $report_body[0][$i]; + } + is( + $report_body[0][10], $stats_total, + 'Reporting period total matches the sum of all the monthly usage statistics' + ); +}; + +my $sushi_response_file_TR_J2 = dirname(__FILE__) . $test_data_base_dir . "/TR_J2.json"; +my $sushi_counter_51_response_TR_J2 = decode_json( read_file($sushi_response_file_TR_J2) ); +my $sushi_counter_TR_J2 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_51_response_TR_J2 } ); + +subtest 'TR_J2 _COUNTER_report_header' => sub { + + plan tests => 42; + + my @report_header = $sushi_counter_TR_J2->_COUNTER_report_header; + + # Header row #1 - Report_Name + is( $report_header[0][0], 'Report_Name', '1st row is report name' ); + is( $report_header[0][1], 'Journal Access Denied', '1st row is report name' ); + is( $report_header[0][2], undef, '1st row is report name' ); + + # Header row #2 - Report_ID + is( $report_header[1][0], 'Report_ID', '2nd row is report name' ); + is( $report_header[1][1], 'TR_J2', '2nd row is report name' ); + is( $report_header[1][2], undef, '2nd row is report name' ); + + # Header row #3 - Release + is( $report_header[2][0], 'Release', '3rd row is counter release' ); + is( $report_header[2][1], '5.1', '3rd row is counter release' ); + is( $report_header[2][2], undef, '3rd row is counter release' ); + + # Header row #4 - Institution_Name + is( $report_header[3][0], 'Institution_Name', '4th row is institution name' ); + is( $report_header[3][1], 'Sample Institution', '4th row is institution name' ); + is( $report_header[3][2], undef, '4th row is institution name' ); + + # Header row #5 - Institution_ID + is( $report_header[4][0], 'Institution_ID', '5th row is institution id' ); + is( $report_header[4][1], 'ISNI:1234123412341234', '5th row is institution id' ); + is( $report_header[4][2], undef, '5th row is institution id' ); + + # Header row #6 - Metric_Types + is( $report_header[5][0], 'Metric_Types', '6th row is metric types' ); + is( $report_header[5][1], 'Limit_Exceeded; No_License', '6th row is metric types' ); + is( $report_header[5][2], undef, '6th row is metric types' ); + + # Header row #7 - Report_Filters + is( $report_header[6][0], 'Report_Filters', '7th row is report filters' ); + like( $report_header[6][1], qr/Metric_Type:Limit_Exceeded\|No_License/, '7th row is report filters' ); + like( $report_header[6][1], qr/Begin_Date:2022-01-01/, '7th row is report filters' ); + like( $report_header[6][1], qr/End_Date:2022-03-31/, '7th row is report filters' ); + like( $report_header[6][1], qr/Access_Method:Regular/, '7th row is report filters' ); + like( $report_header[6][1], qr/Data_Type:Journal/, '7th row is report filters' ); + + is( $report_header[6][2], undef, '7th row is report filters' ); + + # Header row #8 - Report_Attributes + is( $report_header[7][0], 'Report_Attributes', '8th row is report attributes' ); + is( $report_header[7][1], '', '8th row is report attributes' ); + is( $report_header[7][2], undef, '8th row is report attributes' ); + + # Header row #9 - Exceptions + is( $report_header[8][0], 'Exceptions', '9th row is exceptions' ); + is( $report_header[8][1], '', '9th row is exceptions' ); + is( $report_header[8][2], undef, '9th row is exceptions' ); + + # Header row #10 - Reporting_Period + is( $report_header[9][0], 'Reporting_Period', '10th row is reporting period' ); + is( $report_header[9][1], 'Begin_Date=2022-01-01; End_Date=2022-03-31', '10th row is reporting period' ); + is( $report_header[9][2], undef, '10th row is reporting period' ); + + # Header row #11 - Created + is( $report_header[10][0], 'Created', '11th row is created' ); + is( $report_header[10][1], '2023-02-15T09:11:12Z', '11th row is created' ); + is( $report_header[10][2], undef, '11th row is created' ); + + # Header row #12 - Created + is( $report_header[11][0], 'Created_By', '12th row is created by' ); + is( $report_header[11][1], 'Sample Publisher', '12th row is created by' ); + is( $report_header[11][2], undef, '12th row is created by' ); + + # Header row #13 - Registry_Record + is( $report_header[12][0], 'Registry_Record', '13th row is Registry_Record' ); + + # Header row #14 - This needs to be empty + is( $report_header[13][0], '', '14th row is empty' ); +}; + +subtest 'TR_J2 _COUNTER_report_column_headings' => sub { + + plan tests => 15; + + my @report_column_headings = $sushi_counter_TR_J2->_COUNTER_report_column_headings; + + # Standard TR_J2 column headings + is( $report_column_headings[0][0], 'Title', '1st column heading is title' ); + is( $report_column_headings[0][1], 'Publisher', '2nd column heading is publisher' ); + is( $report_column_headings[0][2], 'Publisher_ID', '3rd column heading is publisher ID' ); + is( $report_column_headings[0][3], 'Platform', '4th column heading is platform' ); + is( $report_column_headings[0][4], 'DOI', '5th column heading is DOI' ); + is( $report_column_headings[0][5], 'Proprietary_ID', '6th column heading is proprietary ID' ); + is( $report_column_headings[0][6], 'Print_ISSN', '7th column heading is print ISSN' ); + is( $report_column_headings[0][7], 'Online_ISSN', '8th column heading is online ISSN' ); + is( $report_column_headings[0][8], 'URI', '9th column heading is URI' ); + is( $report_column_headings[0][9], 'Metric_Type', '10th column heading is metric type' ); + is( $report_column_headings[0][10], 'Reporting_Period_Total', '11th column heading is reporting period total' ); + + # Months column headings + is( $report_column_headings[0][11], 'Jan 2022', '12th column is month column heading' ); + is( $report_column_headings[0][12], 'Feb 2022', '13th column is another month column heading' ); + is( $report_column_headings[0][13], 'Mar 2022', '14th column is the last month column heading' ); + is( $report_column_headings[0][14], undef, '15th column is empty, no more months' ); +}; + +subtest 'TR_J2 _COUNTER_report_body' => sub { + + plan tests => 16; + + my @report_body = $sushi_counter_TR_J2->_COUNTER_report_body; + + # The same title is sequential but for different metric types + is( $report_body[0][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[1][0], 'Title 3', 'same title, same metric type' ); + is( $report_body[0][9], 'Limit_Exceeded', '1st title, different metric_type' ); + is( $report_body[1][9], 'No_License', '1sd title, different metric_type' ); + + # The data is in the correct column + is( $report_body[2][0], 'Another title', '1st column is title' ); + is( $report_body[2][1], 'Another publisher', '2nd column is publisher' ); + is( $report_body[2][2], '4321432143214321', '3rd column is publisher ID and shows because there is ISNI' ); + is( $report_body[2][3], 'Platform 1', '4th column is platform' ); + is( $report_body[2][4], '10.9999/xxxxt03', '5th column is DOI' ); + is( $report_body[2][5], 'P1:T03', '6th column is proprietary ID' ); + is( $report_body[2][6], '', '7th column is print ISSN' ); + is( $report_body[2][7], '1234-4321', '8th column is online ISSN' ); + is( $report_body[2][8], '', '9th column is URI' ); + is( $report_body[2][9], 'Limit_Exceeded', '10th column is metric type' ); + is( $report_body[2][10], 388, '11th column is reporting period total' ); + + # The period total is the sum of all the month columns + my $stats_total = 0; + for ( my $i = 11 ; $i < 14 ; $i++ ) { + $stats_total += $report_body[0][$i]; + } + + is( + $report_body[0][10], $stats_total, + 'Reporting period total matches the sum of all the monthly usage statistics' + ); +}; + +my $sushi_response_file_TR_J3 = dirname(__FILE__) . $test_data_base_dir . "/TR_J3.json"; +my $sushi_counter_51_response_TR_J3 = decode_json( read_file($sushi_response_file_TR_J3) ); +my $sushi_counter_TR_J3 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_51_response_TR_J3 } ); + +subtest 'TR_J3 _COUNTER_report_header' => sub { + + plan tests => 42; + + my @report_header = $sushi_counter_TR_J3->_COUNTER_report_header; + + # Header row #1 - Report_Name + is( $report_header[0][0], 'Report_Name', '1st row is report name' ); + is( $report_header[0][1], 'Journal Usage by Access Type', '1st row is report name' ); + is( $report_header[0][2], undef, '1st row is report name' ); + + # Header row #2 - Report_ID + is( $report_header[1][0], 'Report_ID', '2nd row is report name' ); + is( $report_header[1][1], 'TR_J3', '2nd row is report name' ); + is( $report_header[1][2], undef, '2nd row is report name' ); + + # Header row #3 - Release + is( $report_header[2][0], 'Release', '3rd row is counter release' ); + is( $report_header[2][1], '5.1', '3rd row is counter release' ); + is( $report_header[2][2], undef, '3rd row is counter release' ); + + # Header row #4 - Institution_Name + is( $report_header[3][0], 'Institution_Name', '4th row is institution name' ); + is( $report_header[3][1], 'Sample Institution', '4th row is institution name' ); + is( $report_header[3][2], undef, '4th row is institution name' ); + + # Header row #5 - Institution_ID + is( $report_header[4][0], 'Institution_ID', '5th row is institution id' ); + is( $report_header[4][1], 'ISNI:1234123412341234', '5th row is institution id' ); + is( $report_header[4][2], undef, '5th row is institution id' ); + + # Header row #6 - Metric_Types + is( $report_header[5][0], 'Metric_Types', '6th row is metric types' ); + is( + $report_header[5][1], + 'Total_Item_Investigations; Total_Item_Requests; Unique_Item_Investigations; Unique_Item_Requests', + '6th row is metric types' + ); + is( $report_header[5][2], undef, '6th row is metric types' ); + + # Header row #7 - Report_Filters + is( $report_header[6][0], 'Report_Filters', '7th row is report filters' ); + like( + $report_header[6][1], + qr/Metric_Type:Total_Item_Investigations\|Total_Item_Requests\|Unique_Item_Investigations\|Unique_Item_Requests/, + '7th row is report filters' + ); + like( $report_header[6][1], qr/Access_Method:Regular/, '7th row is report filters' ); + like( $report_header[6][1], qr/End_Date:2022-03-31/, '7th row is report filters' ); + like( $report_header[6][1], qr/Data_Type:Journal/, '7th row is report filters' ); + like( $report_header[6][1], qr/Begin_Date:2022-01-01/, '7th row is report filters' ); + + is( $report_header[6][2], undef, '7th row is report filters' ); + + # Header row #8 - Report_Attributes + is( $report_header[7][0], 'Report_Attributes', '8th row is report attributes' ); + is( $report_header[7][1], '', '8th row is report attributes' ); + is( $report_header[7][2], undef, '8th row is report attributes' ); + + # Header row #9 - Exceptions + is( $report_header[8][0], 'Exceptions', '9th row is exceptions' ); + is( $report_header[8][1], '', '9th row is exceptions' ); + is( $report_header[8][2], undef, '9th row is exceptions' ); + + # Header row #10 - Reporting_Period + is( $report_header[9][0], 'Reporting_Period', '10th row is reporting period' ); + is( $report_header[9][1], 'Begin_Date=2022-01-01; End_Date=2022-03-31', '10th row is reporting period' ); + is( $report_header[9][2], undef, '10th row is reporting period' ); + + # Header row #11 - Created + is( $report_header[10][0], 'Created', '11th row is created' ); + is( $report_header[10][1], '2023-02-15T09:11:12Z', '11th row is created' ); + is( $report_header[10][2], undef, '11th row is created' ); + + # Header row #12 - Created + is( $report_header[11][0], 'Created_By', '12th row is created by' ); + is( $report_header[11][1], 'Sample Publisher', '12th row is created by' ); + is( $report_header[11][2], undef, '12th row is created by' ); + + # Header row #13 - Registry_Record + is( $report_header[12][0], 'Registry_Record', '13th row is Registry_Record' ); + + # Header row #14 - This needs to be empty + is( $report_header[13][0], '', '14th row is empty' ); +}; + +subtest 'TR_J3 _COUNTER_report_column_headings' => sub { + + plan tests => 16; + + my @report_column_headings = $sushi_counter_TR_J3->_COUNTER_report_column_headings; + + # Standard TR_J3 column headings + is( $report_column_headings[0][0], 'Title', '1st column heading is title' ); + is( $report_column_headings[0][1], 'Publisher', '2nd column heading is publisher' ); + is( $report_column_headings[0][2], 'Publisher_ID', '3rd column heading is publisher ID' ); + is( $report_column_headings[0][3], 'Platform', '4th column heading is platform' ); + is( $report_column_headings[0][4], 'DOI', '5th column heading is DOI' ); + is( $report_column_headings[0][5], 'Proprietary_ID', '6th column heading is proprietary ID' ); + is( $report_column_headings[0][6], 'Print_ISSN', '7th column heading is print ISSN' ); + is( $report_column_headings[0][7], 'Online_ISSN', '8th column heading is online ISSN' ); + is( $report_column_headings[0][8], 'URI', '9th column heading is URI' ); + is( $report_column_headings[0][9], 'Access_Type', '10th column heading is access type' ); + is( $report_column_headings[0][10], 'Metric_Type', '11th column heading is access type' ); + is( $report_column_headings[0][11], 'Reporting_Period_Total', '12th column heading is reporting period total' ); + + # Months column headings + is( $report_column_headings[0][12], 'Jan 2022', '12th column is month column heading' ); + is( $report_column_headings[0][13], 'Feb 2022', '13th column is another month column heading' ); + is( $report_column_headings[0][14], 'Mar 2022', '13th column is the last month column heading' ); + is( $report_column_headings[0][25], undef, '14th column is empty, no more months' ); +}; + +subtest 'TR_J3 _COUNTER_report_body' => sub { + + plan tests => 25; + + my @report_body = $sushi_counter_TR_J3->_COUNTER_report_body; + + # The same title is sequential but for different metric types + is( $report_body[0][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[1][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[2][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[3][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[0][9], 'Controlled', 'same title, same access_type' ); + is( $report_body[1][9], 'Controlled', 'same title, same access_type' ); + is( $report_body[2][9], 'Controlled', 'same title, same access_type' ); + is( $report_body[3][9], 'Controlled', 'same title, same access_type' ); + is( $report_body[0][10], 'Total_Item_Investigations', 'same title, different metric type' ); + is( $report_body[1][10], 'Total_Item_Requests', 'same title, different metric type' ); + is( $report_body[2][10], 'Unique_Item_Investigations', 'same title, different metric type' ); + is( $report_body[3][10], 'Unique_Item_Requests', 'same title, different metric type' ); + + # The data is in the correct column + is( $report_body[2][0], 'Title 3', '1st column is title' ); + is( $report_body[2][1], 'Sample Publisher', '2nd column is publisher' ); + is( $report_body[2][2], '4321432143214321', '3rd column is publisher ID' ); + is( $report_body[2][3], 'Platform 1', '4th column is platform' ); + is( $report_body[2][4], '10.9999/xxxxt03', '5th column is DOI' ); + is( $report_body[2][5], 'P1:T03', '6th column is proprietary ID' ); + is( $report_body[2][6], '', '7th column is print ISSN' ); + is( $report_body[2][7], '1234-4321', '8th column is online ISSN' ); + is( $report_body[2][8], '', '9th column is URI' ); + is( $report_body[2][9], 'Controlled', '10th column is access type' ); + is( $report_body[2][10], 'Unique_Item_Investigations', '11th column is metric type' ); + is( $report_body[2][11], 1602, '12th column is reporting period total' ); # 608+618+376 + + # The period total is the sum of all the month columns + my $stats_total = 0; + for ( my $i = 12 ; $i < 15 ; $i++ ) { + $stats_total += $report_body[0][$i]; + } + is( + $report_body[0][11], $stats_total, + 'Reporting period total matches the sum of all the monthly usage statistics' + ); +}; + +my $sushi_response_file_TR_J4 = dirname(__FILE__) . $test_data_base_dir . "/TR_J4.json"; +my $sushi_counter_51_response_TR_J4 = decode_json( read_file($sushi_response_file_TR_J4) ); +my $sushi_counter_TR_J4 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_51_response_TR_J4 } ); + +subtest 'TR_J4 _COUNTER_report_header' => sub { + + plan tests => 43; + + my @report_header = $sushi_counter_TR_J4->_COUNTER_report_header; + + # Header row #1 - Report_Name + is( $report_header[0][0], 'Report_Name', '1st row is report name' ); + is( $report_header[0][1], 'Journal Requests by YOP (Controlled)', '1st row is report name' ); + is( $report_header[0][2], undef, '1st row is report name' ); + + # Header row #2 - Report_ID + is( $report_header[1][0], 'Report_ID', '2nd row is report name' ); + is( $report_header[1][1], 'TR_J4', '2nd row is report name' ); + is( $report_header[1][2], undef, '2nd row is report name' ); + + # Header row #3 - Release + is( $report_header[2][0], 'Release', '3rd row is counter release' ); + is( $report_header[2][1], '5.1', '3rd row is counter release' ); + is( $report_header[2][2], undef, '3rd row is counter release' ); + + # Header row #4 - Institution_Name + is( $report_header[3][0], 'Institution_Name', '4th row is institution name' ); + is( $report_header[3][1], 'Sample Institution', '4th row is institution name' ); + is( $report_header[3][2], undef, '4th row is institution name' ); + + # Header row #5 - Institution_ID + is( $report_header[4][0], 'Institution_ID', '5th row is institution id' ); + is( $report_header[4][1], 'ISNI:1234123412341234', '5th row is institution id' ); + is( $report_header[4][2], undef, '5th row is institution id' ); + + # Header row #6 - Metric_Types + is( $report_header[5][0], 'Metric_Types', '6th row is metric types' ); + is( + $report_header[5][1], + 'Total_Item_Requests; Unique_Item_Requests', + '6th row is metric types' + ); + is( $report_header[5][2], undef, '6th row is metric types' ); + + # Header row #7 - Report_Filters + is( $report_header[6][0], 'Report_Filters', '7th row is report filters' ); + like( $report_header[6][1], qr/Metric_Type:Total_Item_Requests|Unique_Item_Requests/, '7th row is report filters' ); + like( $report_header[6][1], qr/Access_Method:Regular/, '7th row is report filters' ); + like( $report_header[6][1], qr/Begin_Date:2022-01-01/, '7th row is report filters' ); + like( $report_header[6][1], qr/Access_Type:Controlled/, '7th row is report filters' ); + like( $report_header[6][1], qr/End_Date:2022-03-31/, '7th row is report filters' ); + like( $report_header[6][1], qr/Data_Type:Journal/, '7th row is report filters' ); + + is( $report_header[6][2], undef, '7th row is report filters' ); + + # Header row #8 - Report_Attributes + is( $report_header[7][0], 'Report_Attributes', '8th row is report attributes' ); + is( $report_header[7][1], '', '8th row is report attributes' ); + is( $report_header[7][2], undef, '8th row is report attributes' ); + + # Header row #9 - Exceptions + is( $report_header[8][0], 'Exceptions', '9th row is exceptions' ); + is( $report_header[8][1], '', '9th row is exceptions' ); + is( $report_header[8][2], undef, '9th row is exceptions' ); + + # Header row #10 - Reporting_Period + is( $report_header[9][0], 'Reporting_Period', '10th row is reporting period' ); + is( $report_header[9][1], 'Begin_Date=2022-01-01; End_Date=2022-03-31', '10th row is reporting period' ); + is( $report_header[9][2], undef, '10th row is reporting period' ); + + # Header row #11 - Created + is( $report_header[10][0], 'Created', '11th row is created' ); + is( $report_header[10][1], '2023-02-15T09:11:12Z', '11th row is created' ); + is( $report_header[10][2], undef, '11th row is created' ); + + # Header row #12 - Created + is( $report_header[11][0], 'Created_By', '12th row is created by' ); + is( $report_header[11][1], 'Sample Publisher', '12th row is created by' ); + is( $report_header[11][2], undef, '12th row is created by' ); + + # Header row #13 - Registry_Record + is( $report_header[12][0], 'Registry_Record', '13th row is Registry_Record' ); + + # Header row #14 - This needs to be empty + is( $report_header[13][0], '', '14th row is empty' ); +}; + +subtest 'TR_J4 _COUNTER_report_column_headings' => sub { + + plan tests => 16; + + my @report_column_headings = $sushi_counter_TR_J4->_COUNTER_report_column_headings; + + # Standard TR_J4 column headings + is( $report_column_headings[0][0], 'Title', '1st column heading is title' ); + is( $report_column_headings[0][1], 'Publisher', '2nd column heading is publisher' ); + is( $report_column_headings[0][2], 'Publisher_ID', '3rd column heading is publisher ID' ); + is( $report_column_headings[0][3], 'Platform', '4th column heading is platform' ); + is( $report_column_headings[0][4], 'DOI', '5th column heading is DOI' ); + is( $report_column_headings[0][5], 'Proprietary_ID', '6th column heading is proprietary ID' ); + is( $report_column_headings[0][6], 'Print_ISSN', '7th column heading is print ISSN' ); + is( $report_column_headings[0][7], 'Online_ISSN', '8th column heading is online ISSN' ); + is( $report_column_headings[0][8], 'URI', '9th column heading is URI' ); + is( $report_column_headings[0][9], 'YOP', '10th column heading is yop' ); + is( $report_column_headings[0][10], 'Metric_Type', '11th column heading is metric type' ); + is( $report_column_headings[0][11], 'Reporting_Period_Total', '12th column heading is reporting period total' ); + + # Months column headings + is( $report_column_headings[0][12], 'Jan 2022', '13th column is month column heading' ); + is( $report_column_headings[0][13], 'Feb 2022', '14th column is another month column heading' ); + is( $report_column_headings[0][14], 'Mar 2022', '15th column is the last month column heading' ); + is( $report_column_headings[0][15], undef, '16th column is empty, no more months' ); +}; + +subtest 'TR_J4 _COUNTER_report_body' => sub { + + plan tests => 25; + + my @report_body = $sushi_counter_TR_J4->_COUNTER_report_body; + + # The same title is sequential but for different metric types + is( $report_body[0][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[1][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[2][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[3][0], 'Title 3', 'same title, different metric type' ); + is( $report_body[0][10], 'Total_Item_Requests', '1st title, same yop' ); + is( $report_body[1][10], 'Unique_Item_Requests', '1st title, same yop' ); + is( $report_body[2][10], 'Total_Item_Requests', '1st title, same yop' ); + is( $report_body[3][10], 'Unique_Item_Requests', '1st title, same yop' ); + is( $report_body[0][9], '2022', '1st title, yop' ); + is( $report_body[1][9], '2022', '1st title, yop' ); + is( $report_body[2][9], '2021', '1st title, yop' ); + is( $report_body[3][9], '2021', '1st title, yop' ); + + # The data is in the correct column + is( $report_body[2][0], 'Title 3', '1st column is title' ); + is( $report_body[2][1], 'Sample Publisher', '2nd column is publisher' ); + is( $report_body[2][2], '4321432143214321', '3rd column is publisher ID' ); + is( $report_body[2][3], 'Platform 1', '4th column is platform' ); + is( $report_body[2][4], '10.9999/xxxxt03', '5th column is DOI' ); + is( $report_body[2][5], 'P1:T03', '6th column is proprietary ID' ); + is( $report_body[2][6], '', '7th column is print ISSN' ); + is( $report_body[2][7], '1234-4321', '8th column is online ISSN' ); + is( $report_body[2][8], '', '9th column is URI' ); + is( $report_body[2][9], '2021', '10th column is yop' ); + is( $report_body[2][10], 'Total_Item_Requests', '10th column is metric type' ); + is( $report_body[2][11], 640, '11th column is reporting period total' ); #150+247+243 + + # The period total is the sum of all the month columns + my $stats_total = 0; + for ( my $i = 12 ; $i < 15 ; $i++ ) { + $stats_total += $report_body[0][$i]; + } + is( + $report_body[0][11], $stats_total, + 'Reporting period total matches the sum of all the monthly usage statistics' + ); +}; + +my $sushi_response_file_TR_B3 = dirname(__FILE__) . $test_data_base_dir . "/TR_B3.json"; +my $sushi_counter_51_response_TR_B3 = decode_json( read_file($sushi_response_file_TR_B3) ); +my $sushi_counter_TR_B3 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_51_response_TR_B3 } ); + +subtest 'TR_B3 _COUNTER_report_header' => sub { + + plan tests => 47; + + my @report_header = $sushi_counter_TR_B3->_COUNTER_report_header; + + # Header row #1 - Report_Name + is( $report_header[0][0], 'Report_Name', '1st row is report name' ); + is( $report_header[0][1], 'Book Usage by Access Type', '1st row is report name' ); + is( $report_header[0][2], undef, '1st row is report name' ); + + # Header row #2 - Report_ID + is( $report_header[1][0], 'Report_ID', '2nd row is report name' ); + is( $report_header[1][1], 'TR_B3', '2nd row is report name' ); + is( $report_header[1][2], undef, '2nd row is report name' ); + + # Header row #3 - Release + is( $report_header[2][0], 'Release', '3rd row is counter release' ); + is( $report_header[2][1], '5.1', '3rd row is counter release' ); + is( $report_header[2][2], undef, '3rd row is counter release' ); + + # Header row #4 - Institution_Name + is( $report_header[3][0], 'Institution_Name', '4th row is institution name' ); + is( $report_header[3][1], 'Sample Institution', '4th row is institution name' ); + is( $report_header[3][2], undef, '4th row is institution name' ); + + # Header row #5 - Institution_ID + is( $report_header[4][0], 'Institution_ID', '5th row is institution id' ); + is( $report_header[4][1], 'ISNI:1234123412341234', '5th row is institution id' ); + is( $report_header[4][2], undef, '5th row is institution id' ); + + # Header row #6 - Metric_Types + is( $report_header[5][0], 'Metric_Types', '6th row is metric types' ); + like( $report_header[5][1], qr/Total_Item_Investigations/, '6th row is metric types' ); + like( $report_header[5][1], qr/Total_Item_Requests/, '6th row is metric types' ); + like( $report_header[5][1], qr/Unique_Item_Investigations/, '6th row is metric types' ); + like( $report_header[5][1], qr/Unique_Item_Requests/, '6th row is metric types' ); + like( $report_header[5][1], qr/Unique_Title_Investigations/, '6th row is metric types' ); + like( $report_header[5][1], qr/Unique_Title_Requests/, '6th row is metric types' ); + + is( $report_header[5][2], undef, '6th row is metric types' ); + + # Header row #7 - Report_Filters + is( $report_header[6][0], 'Report_Filters', '7th row is report filters' ); + like( $report_header[6][1], qr/Data_Type:Book/, '7th row is report filters' ); + like( $report_header[6][1], qr/Access_Method:Regular/, '7th row is report filters' ); + like( + $report_header[6][1], + qr/Metric_Type:Total_Item_Investigations\|Total_Item_Requests\|Unique_Item_Investigations\|Unique_Item_Requests\|Unique_Title_Investigations\|Unique_Title_Requests/, + '7th row is report filters' + ); + like( $report_header[6][1], qr/Begin_Date:2022-01-01/, '7th row is report filters' ); + like( $report_header[6][1], qr/End_Date:2022-03-31/, '7th row is report filters' ); + + is( $report_header[6][2], undef, '7th row is report filters' ); + + # Header row #8 - Report_Attributes + is( $report_header[7][0], 'Report_Attributes', '8th row is report attributes' ); + is( $report_header[7][1], '', '8th row is report attributes' ); + is( $report_header[7][2], undef, '8th row is report attributes' ); + + # Header row #9 - Exceptions + is( $report_header[8][0], 'Exceptions', '9th row is exceptions' ); + is( $report_header[8][1], '', '9th row is exceptions' ); + is( $report_header[8][2], undef, '9th row is exceptions' ); + + # Header row #10 - Reporting_Period + is( $report_header[9][0], 'Reporting_Period', '10th row is reporting period' ); + is( $report_header[9][1], 'Begin_Date=2022-01-01; End_Date=2022-03-31', '10th row is reporting period' ); + is( $report_header[9][2], undef, '10th row is reporting period' ); + + # Header row #11 - Created + is( $report_header[10][0], 'Created', '11th row is created' ); + is( $report_header[10][1], '2023-02-15T09:11:12Z', '11th row is created' ); + is( $report_header[10][2], undef, '11th row is created' ); + + # Header row #12 - Created + is( $report_header[11][0], 'Created_By', '12th row is created by' ); + is( $report_header[11][1], 'Sample Publisher', '12th row is created by' ); + is( $report_header[11][2], undef, '12th row is created by' ); + + # Header row #13 - Registry_Record + is( $report_header[12][0], 'Registry_Record', '13th row is Registry_Record' ); + + # Header row #14 - This needs to be empty + is( $report_header[13][0], '', '14th row is empty' ); +}; + +subtest 'TR_B3 _COUNTER_report_column_headings' => sub { + + plan tests => 19; + + my @report_column_headings = $sushi_counter_TR_B3->_COUNTER_report_column_headings; + + # Standard TR_J4 column headings + is( $report_column_headings[0][0], 'Title', '1st column heading is title' ); + is( $report_column_headings[0][1], 'Publisher', '2nd column heading is publisher' ); + is( $report_column_headings[0][2], 'Publisher_ID', '3rd column heading is publisher ID' ); + is( $report_column_headings[0][3], 'Platform', '4th column heading is platform' ); + is( $report_column_headings[0][4], 'DOI', '5th column heading is DOI' ); + is( $report_column_headings[0][5], 'Proprietary_ID', '6th column heading is proprietary ID' ); + is( $report_column_headings[0][6], 'ISBN', '7th column heading is ISBN' ); + is( $report_column_headings[0][7], 'Print_ISSN', '8th column heading is print ISSN' ); + is( $report_column_headings[0][8], 'Online_ISSN', '9th column heading is online ISSN' ); + is( $report_column_headings[0][9], 'URI', '10th column heading is URI' ); + is( $report_column_headings[0][10], 'Data_Type', '11th column heading is data type' ); + is( $report_column_headings[0][11], 'YOP', '12th column heading is yop' ); + is( $report_column_headings[0][12], 'Access_Type', '13th column heading is access type' ); + is( $report_column_headings[0][13], 'Metric_Type', '14th column heading is metric type' ); + is( $report_column_headings[0][14], 'Reporting_Period_Total', '15th column heading is reporting period total' ); + + # Months column headings + is( $report_column_headings[0][15], 'Jan 2022', '16th column is month column heading' ); + is( $report_column_headings[0][16], 'Feb 2022', '17th column another month column heading' ); + is( $report_column_headings[0][17], 'Mar 2022', '18th column is the last month column heading' ); + is( $report_column_headings[0][18], undef, '19th column is empty, no more months' ); +}; + +subtest 'TR_B3 _COUNTER_report_body' => sub { + + plan tests => 49; + + my @report_body = $sushi_counter_TR_B3->_COUNTER_report_body; + + # The same title is sequential but for different metric types + is( + $report_body[0][0], 'Title 1', + 'first title, different metric type' + ); + is( + $report_body[1][0], 'Title 1', + 'first title, different metric type' + ); + is( + $report_body[2][0], 'Title 1', + 'first title, different metric type' + ); + is( + $report_body[3][0], 'Title 1', + 'first title, different metric type' + ); + is( + $report_body[4][0], 'Title 1', + 'first title, different metric type' + ); + is( + $report_body[5][0], 'Title 1', + 'first title, different metric type' + ); + is( + $report_body[6][0], 'Title 7', + 'second title, different metric type' + ); + is( + $report_body[7][0], 'Title 7', + 'second title, different metric type' + ); + is( + $report_body[8][0], 'Title 7', + 'second title, different metric type' + ); + is( $report_body[0][10], 'Book', '6 rows for 1st title' ); + is( $report_body[1][10], 'Book', '6 rows for 1st title' ); + is( $report_body[2][10], 'Book', '6 rows for 1st title' ); + is( $report_body[3][10], 'Book', '6 rows for 1st title' ); + is( $report_body[4][10], 'Book', '6 rows for 1st title' ); + is( $report_body[5][10], 'Book', '6 rows for 1st title' ); + is( $report_body[0][11], '2022', '1st title, yop' ); + is( $report_body[1][11], '2022', '1st title, yop' ); + is( $report_body[2][11], '2022', '1st title,yop' ); + is( $report_body[3][11], '2022', '1st title, 2nd yop has 6 metric types' ); + is( $report_body[4][11], '2022', '1st title, 2nd yop has 6 metric types' ); + is( $report_body[5][11], '2022', '1st title, 2nd yop has 6 metric types' ); + is( $report_body[0][12], 'Controlled', '6 rows for 1st title' ); + is( $report_body[1][12], 'Controlled', '6 rows for 1st title' ); + is( $report_body[2][12], 'Controlled', '6 rows for 1st title' ); + is( $report_body[3][12], 'Controlled', '6 rows for 1st title' ); + is( $report_body[4][12], 'Controlled', '6 rows for 1st title' ); + is( $report_body[5][12], 'Controlled', '6 rows for 1st title' ); + is( $report_body[0][13], 'Total_Item_Investigations', '1st title, metric type' ); + is( $report_body[1][13], 'Total_Item_Requests', '1st title, metric type' ); + is( $report_body[2][13], 'Unique_Item_Investigations', '1st title, metric type' ); + is( $report_body[3][13], 'Unique_Item_Requests', '1st title, metric type' ); + is( $report_body[4][13], 'Unique_Title_Investigations', '1st title, metric type' ); + is( $report_body[5][13], 'Unique_Title_Requests', '1st title, metric type' ); + + # The data is in the correct column + is( $report_body[2][0], 'Title 1', '1st column is title' ); + is( $report_body[2][1], 'Sample Publisher', '2nd column is publisher' ); + is( $report_body[2][2], '4321432143214321', '3rd column is publisher ID' ); + is( $report_body[2][3], 'Platform 1', '4th column is platform' ); + is( $report_body[2][4], '10.9999/xxxxt01', '5th column is DOI' ); + is( $report_body[2][5], 'P1:T01', '6th column is proprietary ID' ); + is( $report_body[2][6], '979-8-88888-888-8', '7th column is ISBN' ); + is( $report_body[2][7], '', '8th column is print ISSN' ); + is( $report_body[2][8], '', '9th column is online ISSN' ); + is( $report_body[2][9], '', '10th column is URI' ); + is( $report_body[2][10], 'Book', '11th column is data_type' ); + is( $report_body[2][11], '2022', '12th column is yop' ); + is( $report_body[2][12], 'Controlled', '12th column is access type' ); + is( $report_body[2][13], 'Unique_Item_Investigations', '13th column is metric type' ); + is( $report_body[2][14], 2760, '14th column is reporting period total' ); #828+935+997 + + # The period total is the sum of all the month columns + my $stats_total = 0; + for ( my $i = 15 ; $i < 18 ; $i++ ) { + $stats_total += $report_body[0][$i]; + } + is( + $report_body[0][14], $stats_total, + 'Reporting period total matches the sum of all the monthly usage statistics' + ); +}; + +my $sushi_response_file_TR_B2 = dirname(__FILE__) . $test_data_base_dir . "/TR_B2.json"; +my $sushi_counter_51_response_TR_B2 = decode_json( read_file($sushi_response_file_TR_B2) ); +my $sushi_counter_TR_B2 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_51_response_TR_B2 } ); + +subtest 'TR_B2 _COUNTER_report_header' => sub { + + plan tests => 42; + + my @report_header = $sushi_counter_TR_B2->_COUNTER_report_header; + + # Header row #1 - Report_Name + is( $report_header[0][0], 'Report_Name', '1st row is report name' ); + is( $report_header[0][1], 'Book Access Denied', '1st row is report name' ); + is( $report_header[0][2], undef, '1st row is report name' ); + + # Header row #2 - Report_ID + is( $report_header[1][0], 'Report_ID', '2nd row is report name' ); + is( $report_header[1][1], 'TR_B2', '2nd row is report name' ); + is( $report_header[1][2], undef, '2nd row is report name' ); + + # Header row #3 - Release + is( $report_header[2][0], 'Release', '3rd row is counter release' ); + is( $report_header[2][1], '5.1', '3rd row is counter release' ); + is( $report_header[2][2], undef, '3rd row is counter release' ); + + # Header row #4 - Institution_Name + is( $report_header[3][0], 'Institution_Name', '4th row is institution name' ); + is( $report_header[3][1], 'Sample Institution', '4th row is institution name' ); + is( $report_header[3][2], undef, '4th row is institution name' ); + + # Header row #5 - Institution_ID + is( $report_header[4][0], 'Institution_ID', '5th row is institution id' ); + is( $report_header[4][1], 'ISNI:1234123412341234', '5th row is institution id' ); + is( $report_header[4][2], undef, '5th row is institution id' ); + + # Header row #6 - Metric_Types + is( $report_header[5][0], 'Metric_Types', '6th row is metric types' ); + is( + $report_header[5][1], + 'Limit_Exceeded; No_License', + '6th row is metric types' + ); + is( $report_header[5][2], undef, '6th row is metric types' ); + + # Header row #7 - Report_Filters + is( $report_header[6][0], 'Report_Filters', '7th row is report filters' ); + like( $report_header[6][1], qr/Data_Type:Book/, '7th row is report filters' ); + like( $report_header[6][1], qr/Access_Method:Regular/, '7th row is report filters' ); + like( $report_header[6][1], qr/Metric_Type:Limit_Exceeded\|No_License/, '7th row is report filters' ); + like( $report_header[6][1], qr/Begin_Date:2022-01-01/, '7th row is report filters' ); + like( $report_header[6][1], qr/End_Date:2022-03-31/, '7th row is report filters' ); + + is( $report_header[6][2], undef, '7th row is report filters' ); + + # Header row #8 - Report_Attributes + is( $report_header[7][0], 'Report_Attributes', '8th row is report attributes' ); + is( $report_header[7][1], '', '8th row is report attributes' ); + is( $report_header[7][2], undef, '8th row is report attributes' ); + + # Header row #9 - Exceptions + is( $report_header[8][0], 'Exceptions', '9th row is exceptions' ); + is( $report_header[8][1], '', '9th row is exceptions' ); + is( $report_header[8][2], undef, '9th row is exceptions' ); + + # Header row #10 - Reporting_Period + is( $report_header[9][0], 'Reporting_Period', '10th row is reporting period' ); + is( $report_header[9][1], 'Begin_Date=2022-01-01; End_Date=2022-03-31', '10th row is reporting period' ); + is( $report_header[9][2], undef, '10th row is reporting period' ); + + # Header row #11 - Created + is( $report_header[10][0], 'Created', '11th row is created' ); + is( $report_header[10][1], '2023-02-15T09:11:12Z', '11th row is created' ); + is( $report_header[10][2], undef, '11th row is created' ); + + # Header row #12 - Created + is( $report_header[11][0], 'Created_By', '12th row is created by' ); + is( $report_header[11][1], 'Sample Publisher', '12th row is created by' ); + is( $report_header[11][2], undef, '12th row is created by' ); + + # Header row #13 - Registry_Record + is( $report_header[12][0], 'Registry_Record', '13th row is Registry_Record' ); + + # Header row #14 - This needs to be empty + is( $report_header[13][0], '', '14th row is empty' ); +}; + +subtest 'TR_B2 _COUNTER_report_column_headings' => sub { + + plan tests => 18; + + my @report_column_headings = $sushi_counter_TR_B2->_COUNTER_report_column_headings; + + # Standard TR_J4 column headings + is( $report_column_headings[0][0], 'Title', '1st column heading is title' ); + is( $report_column_headings[0][1], 'Publisher', '2nd column heading is publisher' ); + is( $report_column_headings[0][2], 'Publisher_ID', '3rd column heading is publisher ID' ); + is( $report_column_headings[0][3], 'Platform', '4th column heading is platform' ); + is( $report_column_headings[0][4], 'DOI', '5th column heading is DOI' ); + is( $report_column_headings[0][5], 'Proprietary_ID', '6th column heading is proprietary ID' ); + is( $report_column_headings[0][6], 'ISBN', '7th column heading is ISBN' ); + is( $report_column_headings[0][7], 'Print_ISSN', '8th column heading is print ISSN' ); + is( $report_column_headings[0][8], 'Online_ISSN', '9th column heading is online ISSN' ); + is( $report_column_headings[0][9], 'URI', '10th column heading is URI' ); + is( $report_column_headings[0][10], 'Data_Type', '11th column heading is data type' ); + is( $report_column_headings[0][11], 'YOP', '12th column heading is yop' ); + is( $report_column_headings[0][12], 'Metric_Type', '13th column heading is metric type' ); + is( $report_column_headings[0][13], 'Reporting_Period_Total', '14th column heading is reporting period total' ); + + # Months column headings + is( $report_column_headings[0][14], 'Jan 2022', '14th column is month column heading' ); + is( $report_column_headings[0][15], 'Feb 2022', '15th column is another column heading' ); + is( $report_column_headings[0][16], 'Mar 2022', '16th column is the last month column heading' ); + is( $report_column_headings[0][17], undef, '17th column is empty, no more months' ); +}; + +subtest 'TR_B2 _COUNTER_report_body' => sub { + + plan tests => 19; + + my @report_body = $sushi_counter_TR_B2->_COUNTER_report_body; + + # The same title is sequential but for different metric types + is( + $report_body[0][0], 'Title 1', + 'same title, different metric type' + ); + is( + $report_body[1][0], 'Title 1', + 'same title, different metric type' + ); + is( $report_body[0][12], 'Limit_Exceeded', '2 rows for 1st title, metric type' ); + is( $report_body[1][12], 'No_License', '2 rows for 1st title, metric type' ); + + # The data is in the correct column + is( $report_body[2][0], 'Title 7', '1st column is title' ); + is( $report_body[2][1], 'Sample Publisher', '2nd column is publisher' ); + is( $report_body[2][2], '4321432143214321', '3rd column heading is publisher ID' ); + is( $report_body[2][3], 'Platform 1', '4th column is platform' ); + is( $report_body[2][4], '10.9999/xxxxt07', '5th column is DOI' ); + is( $report_body[2][5], 'P1:T07', '6th column is proprietary ID' ); + is( $report_body[2][6], '979-8-88888-888-9', '7th column is ISBN' ); + is( $report_body[2][7], '', '8th column is print ISSN' ); + is( $report_body[2][8], '', '9th column is online ISSN' ); + is( $report_body[2][9], '', '10th column is URI' ); + is( $report_body[2][10], 'Reference_Work', '11th column is data type' ); + is( $report_body[2][11], '2019', '11th column is yop' ); + is( $report_body[2][12], 'Limit_Exceeded', '12th column is access type' ); + is( $report_body[2][13], 217, '13th column is reporting period total' ); #84+84+49 + + # The period total is the sum of all the month columns + my $stats_total = 0; + for ( my $i = 14 ; $i < 17 ; $i++ ) { + $stats_total += $report_body[0][$i]; + } + is( + $report_body[0][13], $stats_total, + 'Reporting period total matches the sum of all the monthly usage statistics' + ); +}; diff --git a/t/db_dependent/Koha/ERM/EUsage/CounterFile.t b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5/CounterFile.t similarity index 99% rename from t/db_dependent/Koha/ERM/EUsage/CounterFile.t rename to t/db_dependent/Koha/ERM/EUsage/COUNTER_5/CounterFile.t index 7d36fe82e6f..4465eb71fee 100755 --- a/t/db_dependent/Koha/ERM/EUsage/CounterFile.t +++ b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5/CounterFile.t @@ -35,7 +35,7 @@ my $builder = t::lib::TestBuilder->new; my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); -my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../../data/erm/eusage/TR_J1.json"; +my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../../../data/erm/eusage/COUNTER_5/TR_J1.json"; my $sushi_counter_5_response_TR_J1 = read_file($sushi_response_file_TR_J1); my $sushi_counter_report_TR_J1 = Koha::ERM::EUsage::SushiCounter->new( { response => decode_json($sushi_counter_5_response_TR_J1) } ); diff --git a/t/db_dependent/Koha/ERM/EUsage/SushiCounter.t b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5/SushiCounter.t similarity index 98% rename from t/db_dependent/Koha/ERM/EUsage/SushiCounter.t rename to t/db_dependent/Koha/ERM/EUsage/COUNTER_5/SushiCounter.t index e47568f606a..76241315901 100755 --- a/t/db_dependent/Koha/ERM/EUsage/SushiCounter.t +++ b/t/db_dependent/Koha/ERM/EUsage/COUNTER_5/SushiCounter.t @@ -30,7 +30,8 @@ use t::lib::TestBuilder; my $schema = Koha::Database->new->schema; my $builder = t::lib::TestBuilder->new; -my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../../data/erm/eusage/TR_J1.json"; +my $test_data_base_dir = "/../../../../data/erm/eusage/COUNTER_5"; +my $sushi_response_file_TR_J1 = dirname(__FILE__) . $test_data_base_dir . "/TR_J1.json"; my $sushi_counter_5_response_TR_J1 = decode_json( read_file($sushi_response_file_TR_J1) ); my $sushi_counter_TR_J1 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_5_response_TR_J1 } ); @@ -176,7 +177,7 @@ subtest 'TR_J1 _COUNTER_report_body' => sub { ); }; -my $sushi_response_file_TR_J2 = dirname(__FILE__) . "/../../../data/erm/eusage/TR_J2.json"; +my $sushi_response_file_TR_J2 = dirname(__FILE__) . $test_data_base_dir . "/TR_J2.json"; my $sushi_counter_5_response_TR_J2 = decode_json( read_file($sushi_response_file_TR_J2) ); my $sushi_counter_TR_J2 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_5_response_TR_J2 } ); @@ -315,7 +316,7 @@ subtest 'TR_J2 _COUNTER_report_body' => sub { ); }; -my $sushi_response_file_TR_J3 = dirname(__FILE__) . "/../../../data/erm/eusage/TR_J3.json"; +my $sushi_response_file_TR_J3 = dirname(__FILE__) . $test_data_base_dir . "/TR_J3.json"; my $sushi_counter_5_response_TR_J3 = decode_json( read_file($sushi_response_file_TR_J3) ); my $sushi_counter_TR_J3 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_5_response_TR_J3 } ); @@ -468,7 +469,7 @@ subtest 'TR_J3 _COUNTER_report_body' => sub { ); }; -my $sushi_response_file_TR_J4 = dirname(__FILE__) . "/../../../data/erm/eusage/TR_J4.json"; +my $sushi_response_file_TR_J4 = dirname(__FILE__) . $test_data_base_dir . "/TR_J4.json"; my $sushi_counter_5_response_TR_J4 = decode_json( read_file($sushi_response_file_TR_J4) ); my $sushi_counter_TR_J4 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_5_response_TR_J4 } ); @@ -621,7 +622,7 @@ subtest 'TR_J4 _COUNTER_report_body' => sub { ); }; -my $sushi_response_file_TR_B3 = dirname(__FILE__) . "/../../../data/erm/eusage/TR_B3.json"; +my $sushi_response_file_TR_B3 = dirname(__FILE__) . $test_data_base_dir . "/TR_B3.json"; my $sushi_counter_5_response_TR_B3 = decode_json( read_file($sushi_response_file_TR_B3) ); my $sushi_counter_TR_B3 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_5_response_TR_B3 } ); @@ -829,7 +830,7 @@ subtest 'TR_B3 _COUNTER_report_body' => sub { ); }; -my $sushi_response_file_TR_B2 = dirname(__FILE__) . "/../../../data/erm/eusage/TR_B2.json"; +my $sushi_response_file_TR_B2 = dirname(__FILE__) . $test_data_base_dir . "/TR_B2.json"; my $sushi_counter_5_response_TR_B2 = decode_json( read_file($sushi_response_file_TR_B2) ); my $sushi_counter_TR_B2 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_5_response_TR_B2 } ); diff --git a/t/db_dependent/Koha/ERM/EUsage/UsageDataProvider.t b/t/db_dependent/Koha/ERM/EUsage/UsageDataProvider.t index 941bfe351d3..711e4f71e2b 100755 --- a/t/db_dependent/Koha/ERM/EUsage/UsageDataProvider.t +++ b/t/db_dependent/Koha/ERM/EUsage/UsageDataProvider.t @@ -31,7 +31,7 @@ my $builder = t::lib::TestBuilder->new; subtest '_build_url_query' => sub { - plan tests => 2; + plan tests => 3; $schema->storage->txn_begin; @@ -45,8 +45,8 @@ subtest '_build_url_query' => sub { { class => 'Koha::ERM::EUsage::UsageDataProviders', value => { - service_url => $service_url, api_key => $api_key, requestor_id => $requestor_id, - customer_id => $customer_id, name => $name, service_platform => undef + service_url => $service_url, api_key => $api_key, requestor_id => $requestor_id, + customer_id => $customer_id, name => $name, service_platform => undef, report_release => 5 } } ); @@ -78,8 +78,8 @@ subtest '_build_url_query' => sub { { class => 'Koha::ERM::EUsage::UsageDataProviders', value => { - service_url => $service_url, api_key => $api_key, requestor_id => $requestor_id, - customer_id => $customer_id, name => $name, service_platform => $test_platform + service_url => $service_url, api_key => $api_key, requestor_id => $requestor_id, + customer_id => $customer_id, name => $name, service_platform => $test_platform, report_release => 5 } } ); @@ -107,6 +107,39 @@ subtest '_build_url_query' => sub { . $test_platform ); + my $usage_data_provider_51 = $builder->build_object( + { + class => 'Koha::ERM::EUsage::UsageDataProviders', + value => { + service_url => $service_url, api_key => $api_key, requestor_id => $requestor_id, + customer_id => $customer_id, name => $name, service_platform => $test_platform, report_release => '5.1' + } + } + ); + + $usage_data_provider_51->{report_type} = 'TR_J1'; + $usage_data_provider_51->{begin_date} = '2023-08-01'; + $usage_data_provider_51->{end_date} = '2023-09-30'; + + is( + $usage_data_provider_51->_build_url_query, + $service_url + . '/r51/reports/' + . lc( $usage_data_provider->{report_type} ) + . '?customer_id=' + . $customer_id + . '&requestor_id=' + . $requestor_id + . '&api_key=' + . $api_key + . '&begin_date=' + . substr( $usage_data_provider->{begin_date}, 0, 7 ) + . '&end_date=' + . substr( $usage_data_provider->{end_date}, 0, 7 ) + . '&platform=' + . $test_platform + ); + $schema->storage->txn_rollback; }; diff --git a/t/db_dependent/api/v1/erm_custom_reports.t b/t/db_dependent/api/v1/erm_custom_reports.t index fcb7eba1b02..66fd0b4401c 100755 --- a/t/db_dependent/api/v1/erm_custom_reports.t +++ b/t/db_dependent/api/v1/erm_custom_reports.t @@ -19,7 +19,7 @@ use Modern::Perl; -use Test::More tests => 4; +use Test::More tests => 8; use Test::Mojo; use t::lib::TestBuilder; @@ -40,7 +40,7 @@ my $builder = t::lib::TestBuilder->new; my $t = Test::Mojo->new('Koha::REST::V1'); t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); -my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../data/erm/eusage/TR_J1.json"; +my $sushi_response_file_TR_J1 = dirname(__FILE__) . "/../../data/erm/eusage/COUNTER_5/TR_J1.json"; my $sushi_counter_5_response_TR_J1 = decode_json( read_file($sushi_response_file_TR_J1) ); my $report_items = $sushi_counter_5_response_TR_J1->{Report_Items}; my $sushi_counter_TR_J1 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_5_response_TR_J1 } ); @@ -403,3 +403,366 @@ subtest "provider_rollup_report" => sub { $schema->storage->txn_rollback; }; + +my $sushi_response_file_51_TR_J1 = dirname(__FILE__) . "/../../data/erm/eusage/COUNTER_5.1/TR_J1.json"; +my $sushi_counter_51_response_TR_J1 = decode_json( read_file($sushi_response_file_51_TR_J1) ); +my $sushi_counter_51_TR_J1 = Koha::ERM::EUsage::SushiCounter->new( { response => $sushi_counter_51_response_TR_J1 } ); + +subtest "5.1 monthly_report" => sub { + plan tests => 17; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**28 } + } + ); + my $password = 'thePassword123'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + t::lib::Mocks::mock_userenv( { number => $userid } ); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + # Run a harvest to populate the database with data + my $usage_data_provider = $builder->build_object( { class => 'Koha::ERM::EUsage::UsageDataProviders' } ); + my $counter_file = $sushi_counter_51_TR_J1->get_COUNTER_from_SUSHI; + + $usage_data_provider->counter_files( + [ + { + usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id, + file_content => $counter_file, + filename => "Test_TR_J1", + } + ] + ); + + # Unauthorized access + $t->get_ok("//$unauth_userid:$password@/api/v1/erm/eUsage/monthly_report/title")->status_is(403); + + # Authorised access + my $query_string_with_no_results = 'q=[ + { + "erm_usage_muses.year":2023, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[1,2,3,4,5,6,7,8,9,10,11,12], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/monthly_report/title?$query_string_with_no_results" => + { 'x-koha-embed' => 'erm_usage_muses' } )->status_is(200)->json_is( [] ); + + my $query_string = 'q=[ + { + "erm_usage_muses.year":2022, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[6], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + my $expected_results1 = 2; # One title, with one row for each metric type + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/monthly_report/title?$query_string" => + { 'x-koha-embed' => 'erm_usage_muses' } )->status_is(200) + ->json_has( '/' . ( $expected_results1 - 2 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results1 - 1 ) . '/title_id' ) + ->json_hasnt( '/' . ($expected_results1) . '/title_id' ); + + my $query_string_with_multiple_years = 'q=[ + { + "erm_usage_muses.year":2022, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[1,6,7,8], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + }, + { + "erm_usage_muses.year":2021, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[1,2,3], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + my $expected_results2 = 4; # Two titles this time + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/monthly_report/title?$query_string_with_multiple_years" => + { 'x-koha-embed' => 'erm_usage_muses' } )->status_is(200) + ->json_has( '/' . ( $expected_results2 - 4 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results2 - 3 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results2 - 2 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results2 - 1 ) . '/title_id' ) + ->json_hasnt( '/' . ($expected_results2) . '/title_id' ); + + $schema->storage->txn_rollback; +}; + +subtest "5.1 yearly_report" => sub { + plan tests => 17; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**28 } + } + ); + my $password = 'thePassword123'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + t::lib::Mocks::mock_userenv( { number => $userid } ); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + # Run a harvest to populate the database with data + my $usage_data_provider = $builder->build_object( { class => 'Koha::ERM::EUsage::UsageDataProviders' } ); + my $counter_file = $sushi_counter_51_TR_J1->get_COUNTER_from_SUSHI; + + $usage_data_provider->counter_files( + [ + { + usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id, + file_content => $counter_file, + filename => "Test_TR_J1", + } + ] + ); + + # Unauthorized access + $t->get_ok("//$unauth_userid:$password@/api/v1/erm/eUsage/yearly_report/title")->status_is(403); + + # Authorised access + my $query_string_with_no_results = 'q= + { + "erm_usage_yuses.year":[2023], + "erm_usage_yuses.report_type":"TR_J1", + "erm_usage_yuses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + '; + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/yearly_report/title?$query_string_with_no_results" => + { 'x-koha-embed' => 'erm_usage_yuses' } )->status_is(200)->json_is( [] ); + + my $query_string = 'q= + { + "erm_usage_yuses.year":[2022], + "erm_usage_yuses.report_type":"TR_J1", + "erm_usage_yuses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + '; + my $expected_results1 = 4; # Two titles, with one row for each metric type + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/yearly_report/title?$query_string" => + { 'x-koha-embed' => 'erm_usage_yuses' } )->status_is(200) + ->json_has( '/' . ( $expected_results1 - 2 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results1 - 1 ) . '/title_id' ) + ->json_hasnt( '/' . ($expected_results1) . '/title_id' ); + + my $query_string_with_multiple_years = 'q= + { + "erm_usage_yuses.year":[2021,2022], + "erm_usage_yuses.report_type":"TR_J1", + "erm_usage_yuses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + '; + my $expected_results2 = 4; # Two titles this time + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/yearly_report/title?$query_string_with_multiple_years" => + { 'x-koha-embed' => 'erm_usage_yuses' } )->status_is(200) + ->json_has( '/' . ( $expected_results2 - 4 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results2 - 3 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results2 - 2 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results2 - 1 ) . '/title_id' ) + ->json_hasnt( '/' . ($expected_results2) . '/title_id' ); + + $schema->storage->txn_rollback; +}; + +subtest "5.1 metric_types_report" => sub { + plan tests => 14; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**28 } + } + ); + my $password = 'thePassword123'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + t::lib::Mocks::mock_userenv( { number => $userid } ); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + # Run a harvest to populate the database with data + my $usage_data_provider = $builder->build_object( { class => 'Koha::ERM::EUsage::UsageDataProviders' } ); + my $counter_file = $sushi_counter_51_TR_J1->get_COUNTER_from_SUSHI; + + $usage_data_provider->counter_files( + [ + { + usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id, + file_content => $counter_file, + filename => "Test_TR_J1", + } + ] + ); + + # Unauthorized access + $t->get_ok("//$unauth_userid:$password@/api/v1/erm/eUsage/metric_types_report/title")->status_is(403); + + # Authorised access + my $query_string_with_no_results = 'q=[ + { + "erm_usage_muses.year":2023, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[1,2,3,4,5,6,7,8,9,10,11,12], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/metric_types_report/title?$query_string_with_no_results" => + { 'x-koha-embed' => 'erm_usage_muses' } )->status_is(200)->json_is( [] ); + + my $query_string = 'q=[ + { + "erm_usage_muses.year":2022, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[6], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + my $expected_results1 = 1; # One title + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/metric_types_report/title?$query_string" => + { 'x-koha-embed' => 'erm_usage_muses' } )->status_is(200) + ->json_has( '/' . ( $expected_results1 - 1 ) . '/title_id' ) + ->json_hasnt( '/' . ($expected_results1) . '/title_id' ); + + my $query_string_with_multiple_years = 'q=[ + { + "erm_usage_muses.year":2022, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[1,2,3,6,7,8], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + }, + { + "erm_usage_muses.year":2021, + "erm_usage_muses.report_type":"TR_J1", + "erm_usage_muses.month":[1,2,3], + "erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + my $expected_results2 = 2; # Two titles this time + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/metric_types_report/title?$query_string_with_multiple_years" => + { 'x-koha-embed' => 'erm_usage_muses' } )->status_is(200) + ->json_has( '/' . ( $expected_results2 - 2 ) . '/title_id' ) + ->json_has( '/' . ( $expected_results2 - 1 ) . '/title_id' ) + ->json_hasnt( '/' . ($expected_results2) . '/title_id' ); + + $schema->storage->txn_rollback; +}; + +subtest "5.1 provider_rollup_report" => sub { + plan tests => 9; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**28 } + } + ); + my $password = 'thePassword123'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + t::lib::Mocks::mock_userenv( { number => $userid } ); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + # Run a harvest to populate the database with data + my $usage_data_provider = $builder->build_object( { class => 'Koha::ERM::EUsage::UsageDataProviders' } ); + my $counter_file = $sushi_counter_51_TR_J1->get_COUNTER_from_SUSHI; + + $usage_data_provider->counter_files( + [ + { + usage_data_provider_id => $usage_data_provider->erm_usage_data_provider_id, + file_content => $counter_file, + filename => "Test_TR_J1", + } + ] + ); + + # Unauthorized access + $t->get_ok("//$unauth_userid:$password@/api/v1/erm/eUsage/provider_rollup_report/title")->status_is(403); + + # Authorised access + my $query_string_with_no_results = 'q=[ + { + "erm_usage_titles.erm_usage_muses.year":2023, + "erm_usage_titles.erm_usage_muses.report_type":"TR_J1", + "erm_usage_titles.erm_usage_muses.month":[1,2,3,4,5,6,7,8,9,10,11,12], + "erm_usage_titles.erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/provider_rollup_report/title?$query_string_with_no_results" => + { 'x-koha-embed' => 'erm_usage_titles.erm_usage_muses' } )->status_is(200)->json_is( [] ); + + my $query_string = 'q=[ + { + "erm_usage_titles.erm_usage_muses.year":2022, + "erm_usage_titles.erm_usage_muses.report_type":"TR_J1", + "erm_usage_titles.erm_usage_muses.month":[1,6], + "erm_usage_titles.erm_usage_muses.metric_type":["Total_Item_Requests","Unique_Item_Requests"] + } + ]'; + my $expected_results1 = 2; # One provider repeated once for each metric type + + $t->get_ok( "//$userid:$password@/api/v1/erm/eUsage/provider_rollup_report/title?$query_string" => + { 'x-koha-embed' => 'erm_usage_titles.erm_usage_muses' } )->status_is(200) + ->json_is( '/' . ( $expected_results1 - 2 ) . '/provider_rollup_total' => 528, 'Total is correct' ) + ->json_is( '/' . ( $expected_results1 - 1 ) . '/provider_rollup_total' => 228, 'Total is correct' ); + + $schema->storage->txn_rollback; +}; diff --git a/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B2.json b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B2.json new file mode 100644 index 00000000000..10a67ccbdab --- /dev/null +++ b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B2.json @@ -0,0 +1,101 @@ +{ + "Report_Header": { + "Release": "5.1", + "Report_ID": "TR_B2", + "Report_Name": "Book Access Denied", + "Created": "2023-02-15T09:11:12Z", + "Created_By": "Sample Publisher", + "Institution_ID": { + "ISNI": [ + "1234123412341234" + ] + }, + "Institution_Name": "Sample Institution", + "Registry_Record": "https://registry.projectcounter.org/platform/99999999-9999-9999-9999-999999999999", + "Report_Filters": { + "Metric_Type": [ + "Limit_Exceeded", + "No_License" + ], + "Begin_Date": "2022-01-01", + "End_Date": "2022-03-31", + "Data_Type": [ + "Book", + "Reference_Work" + ], + "Access_Method": [ + "Regular" + ] + } + }, + "Report_Items": [ + { + "Title": "Title 1", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt01", + "Proprietary": "P1:T01", + "ISBN": "979-8-88888-888-8", + "URI": "https://doi.org/10.9999/xxxxt01" + }, + "Attribute_Performance": [ + { + "Data_Type": "Book", + "YOP": "2022", + "Performance": { + "Limit_Exceeded": { + "2022-01": 49, + "2022-02": 90, + "2022-03": 40 + }, + "No_License": { + "2022-01": 50, + "2022-02": 84, + "2022-03": 47 + } + } + } + ] + }, + { + "Title": "Title 7", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt07", + "Proprietary": "P1:T07", + "ISBN": "979-8-88888-888-9", + "URI": "https://doi.org/10.9999/xxxxt07" + }, + "Attribute_Performance": [ + { + "Data_Type": "Reference_Work", + "YOP": "2019", + "Performance": { + "Limit_Exceeded": { + "2022-01": 84, + "2022-02": 84, + "2022-03": 49 + }, + "No_License": { + "2022-01": 70, + "2022-02": 74, + "2022-03": 54 + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B3.json b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B3.json new file mode 100644 index 00000000000..945d609359d --- /dev/null +++ b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_B3.json @@ -0,0 +1,147 @@ +{ + "Report_Header": { + "Release": "5.1", + "Report_ID": "TR_B3", + "Report_Name": "Book Usage by Access Type", + "Created": "2023-02-15T09:11:12Z", + "Created_By": "Sample Publisher", + "Institution_ID": { + "ISNI": [ + "1234123412341234" + ] + }, + "Institution_Name": "Sample Institution", + "Registry_Record": "https://registry.projectcounter.org/platform/99999999-9999-9999-9999-999999999999", + "Report_Filters": { + "Metric_Type": [ + "Total_Item_Investigations", + "Total_Item_Requests", + "Unique_Item_Investigations", + "Unique_Item_Requests", + "Unique_Title_Investigations", + "Unique_Title_Requests" + ], + "Begin_Date": "2022-01-01", + "End_Date": "2022-03-31", + "Data_Type": [ + "Book", + "Reference_Work" + ], + "Access_Method": [ + "Regular" + ] + } + }, + "Report_Items": [ + { + "Title": "Title 1", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt01", + "Proprietary": "P1:T01", + "ISBN": "979-8-88888-888-8", + "URI": "https://doi.org/10.9999/xxxxt01" + }, + "Attribute_Performance": [ + { + "Data_Type": "Book", + "YOP": "2022", + "Access_Type": "Controlled", + "Performance": { + "Total_Item_Investigations": { + "2022-01": 1104, + "2022-02": 1246, + "2022-03": 1329 + }, + "Total_Item_Requests": { + "2022-01": 662, + "2022-02": 748, + "2022-03": 797 + }, + "Unique_Item_Investigations": { + "2022-01": 828, + "2022-02": 935, + "2022-03": 997 + }, + "Unique_Item_Requests": { + "2022-01": 497, + "2022-02": 561, + "2022-03": 598 + }, + "Unique_Title_Investigations": { + "2022-01": 552, + "2022-02": 623, + "2022-03": 665 + }, + "Unique_Title_Requests": { + "2022-01": 414, + "2022-02": 467, + "2022-03": 499 + } + } + } + ] + }, + { + "Title": "Title 7", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt07", + "Proprietary": "P1:T07", + "ISBN": "979-8-88888-888-9", + "URI": "https://doi.org/10.9999/xxxxt07" + }, + "Attribute_Performance": [ + { + "Data_Type": "Reference_Work", + "YOP": "2019", + "Access_Type": "Controlled", + "Performance": { + "Total_Item_Investigations": { + "2022-01": 1645, + "2022-02": 1022, + "2022-03": 1106 + }, + "Total_Item_Requests": { + "2022-01": 987, + "2022-02": 613, + "2022-03": 664 + }, + "Unique_Item_Investigations": { + "2022-01": 1234, + "2022-02": 767, + "2022-03": 830 + }, + "Unique_Item_Requests": { + "2022-01": 740, + "2022-02": 460, + "2022-03": 498 + }, + "Unique_Title_Investigations": { + "2022-01": 301, + "2022-02": 283, + "2022-03": 217 + }, + "Unique_Title_Requests": { + "2022-01": 226, + "2022-02": 212, + "2022-03": 163 + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J1.json b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J1.json new file mode 100644 index 00000000000..b38542432e3 --- /dev/null +++ b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J1.json @@ -0,0 +1,104 @@ +{ + "Report_Header": { + "Release": "5.1", + "Report_ID": "TR_J1", + "Report_Name": "Journal Requests (Excluding OA_Gold)", + "Created": "2023-08-29T07:11:41Z", + "Created_By": "Test Systems Inc.", + "Institution_ID": { + "Proprietary": [ + "TInsti:EALTEST001" + ], + "ISNI": [ + "0000000123456789" + ] + }, + "Institution_Name": "Test Institution", + "Registry_Record": "https://registry.projectcounter.org/platform/99999999-9999-9999-9999-999999999999", + "Report_Filters": { + "Metric_Type": [ + "Total_Item_Requests", + "Unique_Item_Requests" + ], + "Begin_Date": "2021-09-01", + "End_Date": "2022-09-30", + "Data_Type": [ + "Journal" + ], + "Access_Type": [ + "Controlled" + ], + "Access_Method": [ + "Regular" + ], + "Data_Type": [ + "Journal" + ] + } + }, + "Report_Items": [ + { + "Title": "Education and Training", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt03", + "Proprietary": "P1:T03", + "Online_ISSN": "1234-4321", + "URI": "https://doi.org/10.9999/xxxxt03" + }, + "Attribute_Performance": [ + { + "Performance": { + "Total_Item_Requests": { + "2022-01": 526, + "2022-02": 866, + "2022-03": 852 + }, + "Unique_Item_Requests": { + "2022-01": 226, + "2022-02": 372, + "2022-03": 366 + } + } + } + ] + }, + { + "Title": "Test Journal", + "Publisher": "Test Publisher", + "Publisher_ID": { + "ISNI": [ + "0000000123456789" + ] + }, + "Platform": "Unit Test Library", + "Item_ID": { + "DOI": "10.1002/(ISSN)1111-2222", + "Proprietary": "TInsti:ABC1", + "Online_ISSN": "5555-6666", + "URI": "https://doi.org/10.9999/xxxxt03", + "Print_ISSN": "7777-8888" + }, + "Attribute_Performance": [ + { + "Performance": { + "Total_Item_Requests": { + "2022-01": 1, + "2022-06": 1 + }, + "Unique_Item_Requests": { + "2022-01": 1, + "2022-06": 1 + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J2.json b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J2.json new file mode 100644 index 00000000000..3d4ccf17e4e --- /dev/null +++ b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J2.json @@ -0,0 +1,96 @@ +{ + "Report_Header": { + "Release": "5.1", + "Report_ID": "TR_J2", + "Report_Name": "Journal Access Denied", + "Created": "2023-02-15T09:11:12Z", + "Created_By": "Sample Publisher", + "Institution_ID": { + "ISNI": [ + "1234123412341234" + ] + }, + "Institution_Name": "Sample Institution", + "Registry_Record": "https://registry.projectcounter.org/platform/99999999-9999-9999-9999-999999999999", + "Report_Filters": { + "Metric_Type": [ + "Limit_Exceeded", + "No_License" + ], + "Begin_Date": "2022-01-01", + "End_Date": "2022-03-31", + "Data_Type": [ + "Journal" + ], + "Access_Method": [ + "Regular" + ] + } + }, + "Report_Items": [ + { + "Title": "Title 3", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt03", + "Proprietary": "P1:T03", + "Online_ISSN": "1234-4321", + "URI": "https://doi.org/10.9999/xxxxt03" + }, + "Attribute_Performance": [ + { + "Performance": { + "Limit_Exceeded": { + "2022-01": 174, + "2022-02": 176, + "2022-03": 90 + }, + "No_License": { + "2022-01": 96, + "2022-02": 160, + "2022-03": 74 + } + } + } + ] + }, + { + "Title": "Another title", + "Publisher": "Another publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt03", + "Proprietary": "P1:T03", + "Online_ISSN": "1234-4321", + "URI": "https://doi.org/10.9999/xxxxt03" + }, + "Attribute_Performance": [ + { + "Performance": { + "Limit_Exceeded": { + "2022-01": 222, + "2022-02": 111, + "2022-03": 55 + }, + "No_License": { + "2022-01": 11, + "2022-02": 22, + "2022-03": 33 + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J3.json b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J3.json new file mode 100644 index 00000000000..d90a45a5044 --- /dev/null +++ b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J3.json @@ -0,0 +1,170 @@ +{ + "Report_Header": { + "Release": "5.1", + "Report_ID": "TR_J3", + "Report_Name": "Journal Usage by Access Type", + "Created": "2023-02-15T09:11:12Z", + "Created_By": "Sample Publisher", + "Institution_ID": { + "ISNI": [ + "1234123412341234" + ] + }, + "Institution_Name": "Sample Institution", + "Registry_Record": "https://registry.projectcounter.org/platform/99999999-9999-9999-9999-999999999999", + "Report_Filters": { + "Metric_Type": [ + "Total_Item_Investigations", + "Total_Item_Requests", + "Unique_Item_Investigations", + "Unique_Item_Requests" + ], + "Begin_Date": "2022-01-01", + "End_Date": "2022-03-31", + "Data_Type": [ + "Journal" + ], + "Access_Method": [ + "Regular" + ] + } + }, + "Report_Items": [ + { + "Title": "Title 3", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt03", + "Proprietary": "P1:T03", + "Online_ISSN": "1234-4321", + "URI": "https://doi.org/10.9999/xxxxt03" + }, + "Attribute_Performance": [ + { + "Access_Type": "Controlled", + "Performance": { + "Total_Item_Investigations": { + "2022-01": 500, + "2022-02": 824, + "2022-03": 812 + }, + "Total_Item_Requests": { + "2022-01": 300, + "2022-02": 494, + "2022-03": 486 + }, + "Unique_Item_Investigations": { + "2022-01": 376, + "2022-02": 618, + "2022-03": 608 + }, + "Unique_Item_Requests": { + "2022-01": 226, + "2022-02": 372, + "2022-03": 366 + } + } + }, + { + "Access_Type": "Open", + "Performance": { + "Total_Item_Investigations": { + "2022-01": 1504, + "2022-02": 2476, + "2022-03": 2434 + }, + "Total_Item_Requests": { + "2022-01": 902, + "2022-02": 1486, + "2022-03": 1462 + }, + "Unique_Item_Investigations": { + "2022-01": 1128, + "2022-02": 1858, + "2022-03": 1826 + }, + "Unique_Item_Requests": { + "2022-01": 676, + "2022-02": 1114, + "2022-03": 1096 + } + } + } + ] + }, + { + "Title": "Title 4", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999", + "Proprietary": "P1:T03", + "Online_ISSN": "1234-4321", + "URI": "https://doi.org/10.9999" + }, + "Attribute_Performance": [ + { + "Access_Type": "Controlled", + "Performance": { + "Total_Item_Investigations": { + "2022-01": 3, + "2022-02": 4, + "2022-03": 9 + }, + "Total_Item_Requests": { + "2022-01": 55, + "2022-02": 44, + "2022-03": 33 + }, + "Unique_Item_Investigations": { + "2022-01": 88, + "2022-02": 55, + "2022-03": 77 + }, + "Unique_Item_Requests": { + "2022-01": 22, + "2022-02": 33, + "2022-03": 44 + } + } + }, + { + "Access_Type": "Open", + "Performance": { + "Total_Item_Investigations": { + "2022-01": 55, + "2022-02": 76, + "2022-03": 12 + }, + "Total_Item_Requests": { + "2022-01": 65, + "2022-02": 34, + "2022-03": 87 + }, + "Unique_Item_Investigations": { + "2022-01": 12, + "2022-02": 66, + "2022-03": 88 + }, + "Unique_Item_Requests": { + "2022-01": 45, + "2022-02": 23, + "2022-03": 76 + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J4.json b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J4.json new file mode 100644 index 00000000000..426cde5d04d --- /dev/null +++ b/t/db_dependent/data/erm/eusage/COUNTER_5.1/TR_J4.json @@ -0,0 +1,83 @@ +{ + "Report_Header": { + "Release": "5.1", + "Report_ID": "TR_J4", + "Report_Name": "Journal Requests by YOP (Controlled)", + "Created": "2023-02-15T09:11:12Z", + "Created_By": "Sample Publisher", + "Institution_ID": { + "ISNI": [ + "1234123412341234" + ] + }, + "Institution_Name": "Sample Institution", + "Registry_Record": "https://registry.projectcounter.org/platform/99999999-9999-9999-9999-999999999999", + "Report_Filters": { + "Metric_Type": [ + "Total_Item_Requests", + "Unique_Item_Requests" + ], + "Begin_Date": "2022-01-01", + "End_Date": "2022-03-31", + "Data_Type": [ + "Journal" + ], + "Access_Type": [ + "Controlled" + ], + "Access_Method": [ + "Regular" + ] + } + }, + "Report_Items": [ + { + "Title": "Title 3", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt03", + "Proprietary": "P1:T03", + "Online_ISSN": "1234-4321", + "URI": "https://doi.org/10.9999/xxxxt03" + }, + "Attribute_Performance": [ + { + "YOP": "2022", + "Performance": { + "Total_Item_Requests": { + "2022-01": 150, + "2022-02": 247, + "2022-03": 243 + }, + "Unique_Item_Requests": { + "2022-01": 113, + "2022-02": 186, + "2022-03": 183 + } + } + }, + { + "YOP": "2021", + "Performance": { + "Total_Item_Requests": { + "2022-01": 150, + "2022-02": 247, + "2022-03": 243 + }, + "Unique_Item_Requests": { + "2022-01": 113, + "2022-02": 186, + "2022-03": 183 + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/t/db_dependent/data/erm/eusage/TR_B2.json b/t/db_dependent/data/erm/eusage/COUNTER_5/TR_B2.json similarity index 100% rename from t/db_dependent/data/erm/eusage/TR_B2.json rename to t/db_dependent/data/erm/eusage/COUNTER_5/TR_B2.json diff --git a/t/db_dependent/data/erm/eusage/TR_B3.json b/t/db_dependent/data/erm/eusage/COUNTER_5/TR_B3.json similarity index 100% rename from t/db_dependent/data/erm/eusage/TR_B3.json rename to t/db_dependent/data/erm/eusage/COUNTER_5/TR_B3.json diff --git a/t/db_dependent/data/erm/eusage/TR_J1.json b/t/db_dependent/data/erm/eusage/COUNTER_5/TR_J1.json similarity index 100% rename from t/db_dependent/data/erm/eusage/TR_J1.json rename to t/db_dependent/data/erm/eusage/COUNTER_5/TR_J1.json diff --git a/t/db_dependent/data/erm/eusage/TR_J2.json b/t/db_dependent/data/erm/eusage/COUNTER_5/TR_J2.json similarity index 100% rename from t/db_dependent/data/erm/eusage/TR_J2.json rename to t/db_dependent/data/erm/eusage/COUNTER_5/TR_J2.json diff --git a/t/db_dependent/data/erm/eusage/TR_J3.json b/t/db_dependent/data/erm/eusage/COUNTER_5/TR_J3.json similarity index 100% rename from t/db_dependent/data/erm/eusage/TR_J3.json rename to t/db_dependent/data/erm/eusage/COUNTER_5/TR_J3.json diff --git a/t/db_dependent/data/erm/eusage/TR_J4.json b/t/db_dependent/data/erm/eusage/COUNTER_5/TR_J4.json similarity index 100% rename from t/db_dependent/data/erm/eusage/TR_J4.json rename to t/db_dependent/data/erm/eusage/COUNTER_5/TR_J4.json diff --git a/t/db_dependent/data/erm/eusage/UnsupportedCOUNTER/UnsupportedCOUNTER.json b/t/db_dependent/data/erm/eusage/UnsupportedCOUNTER/UnsupportedCOUNTER.json new file mode 100644 index 00000000000..eac8d339f15 --- /dev/null +++ b/t/db_dependent/data/erm/eusage/UnsupportedCOUNTER/UnsupportedCOUNTER.json @@ -0,0 +1,101 @@ +{ + "Report_Header": { + "Release": "99", + "Report_ID": "TR_B2", + "Report_Name": "Book Access Denied", + "Created": "2023-02-15T09:11:12Z", + "Created_By": "Sample Publisher", + "Institution_ID": { + "ISNI": [ + "1234123412341234" + ] + }, + "Institution_Name": "Sample Institution", + "Registry_Record": "https://registry.projectcounter.org/platform/99999999-9999-9999-9999-999999999999", + "Report_Filters": { + "Metric_Type": [ + "Limit_Exceeded", + "No_License" + ], + "Begin_Date": "2022-01-01", + "End_Date": "2022-03-31", + "Data_Type": [ + "Book", + "Reference_Work" + ], + "Access_Method": [ + "Regular" + ] + } + }, + "Report_Items": [ + { + "Title": "Title 1", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt01", + "Proprietary": "P1:T01", + "ISBN": "979-8-88888-888-8", + "URI": "https://doi.org/10.9999/xxxxt01" + }, + "Attribute_Performance": [ + { + "Data_Type": "Book", + "YOP": "2022", + "Performance": { + "Limit_Exceeded": { + "2022-01": 49, + "2022-02": 90, + "2022-03": 40 + }, + "No_License": { + "2022-01": 50, + "2022-02": 84, + "2022-03": 47 + } + } + } + ] + }, + { + "Title": "Title 7", + "Publisher": "Sample Publisher", + "Publisher_ID": { + "ISNI": [ + "4321432143214321" + ] + }, + "Platform": "Platform 1", + "Item_ID": { + "DOI": "10.9999/xxxxt07", + "Proprietary": "P1:T07", + "ISBN": "979-8-88888-888-9", + "URI": "https://doi.org/10.9999/xxxxt07" + }, + "Attribute_Performance": [ + { + "Data_Type": "Reference_Work", + "YOP": "2019", + "Performance": { + "Limit_Exceeded": { + "2022-01": 84, + "2022-02": 84, + "2022-03": 49 + }, + "No_License": { + "2022-01": 70, + "2022-02": 74, + "2022-03": 54 + } + } + } + ] + } + ] +} \ No newline at end of file -- 2.39.5