Bugzilla – Attachment 186068 Details for
Bug 39345
Koha must support COUNTER 5.1
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
[24.11.x] Bug 39345: 24.11 SQUASH
2411x-Bug-39345-2411-SQUASH.patch (text/plain), 163.36 KB, created by
Pedro Amorim
on 2025-09-03 09:48:22 UTC
(
hide
)
Description:
[24.11.x] Bug 39345: 24.11 SQUASH
Filename:
MIME Type:
Creator:
Pedro Amorim
Created:
2025-09-03 09:48:22 UTC
Size:
163.36 KB
patch
obsolete
>From 887b7a521763e6f211c07721f357388abbcf8213 Mon Sep 17 00:00:00 2001 >From: Pedro Amorim <pedro.amorim@openfifth.co.uk> >Date: Mon, 18 Aug 2025 12:19:19 +0000 >Subject: [PATCH] [24.11.x] Bug 39345: 24.11 SQUASH > >The following tests must pass: > >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 >prove xt/author/Text_CSV_Various.t > >Signed-off-by: Eric Phetteplace <phette23@gmail.com> >Signed-off-by: Nick Clemens <nick@bywatersolutions.com> >--- > 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 <http://www.gnu.org/licenses>. >+ >+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 <http://www.gnu.org/licenses>. >+ >+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 95c6180f2ae..53208abb017 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 <Koha::Exceptions::ERM::EUsage::CounterFile> 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, formula => 'empty' } ); >- >- $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, formula => 'empty' } ); >+ >+ $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, formula => 'empty' } ); > >- 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: >+ # <staff_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 <http://www.gnu.org/licenses>. >+ >+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 <http://www.gnu.org/licenses>. >+ >+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
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 39345
:
185593
|
185594
|
185595
|
185596
|
185618
|
185619
|
185620
|
185621
|
185658
|
185659
|
185660
|
185661
|
185662
|
185663
|
185664
|
185922
|
185923
|
185924
|
185925
|
185926
|
185927
|
185928
|
186016
|
186042
|
186043
|
186044
|
186060
|
186061
|
186062
|
186063
|
186064
|
186065
|
186066
|
186067
| 186068