From 8987ff1591f05b1f34b8fc9318fad99d59c7a963 Mon Sep 17 00:00:00 2001 From: Matt Blenkinsop Date: Wed, 2 Aug 2023 09:20:23 +0000 Subject: [PATCH] Bug 34587: Abstract reports backend to allow new data types Now that harvesting is possible for platforms, databases and items we need to be able to generate reports for all of these data types. Currently the reporting backend structure is very geared towards titles. Rather than copying this for each different data type, this patch abstracts the code to accept the data type as a url parameter and use that to generate a report based on a given data type Signed-off-by: Jessica Zairo Signed-off-by: Michaela Sieber Signed-off-by: Nick Clemens Signed-off-by: Tomas Cohen Arazi --- Koha/ERM/UsageDataProvider.pm | 41 ++ Koha/REST/V1/ERM/CustomReports.pm | 555 ++++++++++++++++++ Koha/REST/V1/ERM/UsageDataProviders.pm | 88 --- Koha/REST/V1/ERM/UsageTitles.pm | 4 + .../definitions/erm_custom_report.yaml | 64 ++ api/v1/swagger/paths/erm_custom_reports.yaml | 243 ++++++++ api/v1/swagger/swagger.yaml | 12 +- .../ERM/UsageStatisticsReportBuilder.vue | 44 +- .../ERM/UsageStatisticsReportsViewer.vue | 59 +- 9 files changed, 999 insertions(+), 111 deletions(-) create mode 100644 Koha/REST/V1/ERM/CustomReports.pm create mode 100644 api/v1/swagger/definitions/erm_custom_report.yaml create mode 100644 api/v1/swagger/paths/erm_custom_reports.yaml diff --git a/Koha/ERM/UsageDataProvider.pm b/Koha/ERM/UsageDataProvider.pm index 761cd2449f..d3e096cd6c 100644 --- a/Koha/ERM/UsageDataProvider.pm +++ b/Koha/ERM/UsageDataProvider.pm @@ -28,6 +28,11 @@ use base qw(Koha::Object); use Koha::ERM::CounterFile; use Koha::ERM::CounterFiles; +use Koha::ERM::UsageTitles; +use Koha::ERM::UsageItems; +use Koha::ERM::UsagePlatforms; +use Koha::ERM::UsageDatabases; +use Koha::ERM::MonthlyUsages; use Koha::BackgroundJob::ErmSushiHarvester; =head1 NAME @@ -975,6 +980,42 @@ sub erm_usage_muses { +=head3 erm_usage_platforms + +Method to embed erm_usage_platforms to platforms for report formatting + +=cut + +sub erm_usage_platforms { + my ( $self ) = @_; + my $usage_platform_rs = $self->_result->erm_usage_platforms; + return Koha::ERM::UsagePlatforms->_new_from_dbic($usage_platform_rs); +} + +=head3 erm_usage_items + +Method to embed erm_usage_items to items for report formatting + +=cut + +sub erm_usage_items { + my ( $self ) = @_; + my $usage_item_rs = $self->_result->erm_usage_items; + return Koha::ERM::UsageItems->_new_from_dbic($usage_item_rs); +} + +=head3 erm_usage_databases + +Method to embed erm_usage_databases to databases for report formatting + +=cut + +sub erm_usage_databases { + my ( $self ) = @_; + my $usage_database_rs = $self->_result->erm_usage_databases; + return Koha::ERM::UsageDatabases->_new_from_dbic($usage_database_rs); +} + =head3 _type =cut diff --git a/Koha/REST/V1/ERM/CustomReports.pm b/Koha/REST/V1/ERM/CustomReports.pm new file mode 100644 index 0000000000..4286887a1b --- /dev/null +++ b/Koha/REST/V1/ERM/CustomReports.pm @@ -0,0 +1,555 @@ +package Koha::REST::V1::ERM::CustomReports; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use Koha::ERM::UsageTitles; +use Koha::ERM::UsagePlatforms; +use Koha::ERM::UsageItems; +use Koha::ERM::UsageDatabases; +use Koha::ERM::UsageDataProvider; +use Koha::ERM::UsageDataProviders; + +use Clone qw( clone ); +use Scalar::Util qw( blessed ); +use Try::Tiny qw( catch try ); +use JSON; + +=head1 API + +=head2 Methods + +=head3 monthly_report + +An endpoint to fetch filtered monthly report data for ERM usage statistics + +=cut + +sub monthly_report { + my $c = shift->openapi->valid_input or return; + + return try { + + my $args = $c->validation->output; + + my @query_params_array; + my $json = JSON->new; + + if ( ref( $args->{q} ) eq 'ARRAY' ) { + foreach my $q ( @{ $args->{q} } ) { + push @query_params_array, $json->decode($q) + if $q; + } + } + + my $data_type = $c->validation->param('data_type'); + my $data_set = _get_data_set($data_type); + my $data = $c->objects->search($data_set); + + my $usage_data_providers = + Koha::ERM::UsageDataProviders->search( {}, {} )->unblessed; + my $metric_types = + $query_params_array[0][0]->{'erm_usage_muses.metric_type'}; + +# Objects with no data in the selected range will not be returned by the API - we still want to include them if they have been requested + my $requested_ids = + _get_correct_query_param( $data_type, \@query_params_array, + 'monthly' ); + for my $id ( @{$requested_ids} ) { + my $missing_result = _get_result_with_no_statistics( + { + data => $data, + data_type => $data_type, + id => $id, + } + ); + push @{$data}, $missing_result if $missing_result; + } + + my @report_data; + + for my $data_object ( @{$data} ) { + + # Add provider name rather than embed provider object + my $usage_data_provider_id = $data_object->{usage_data_provider_id}; + my @provider_object = grep { + $usage_data_provider_id eq $_->{erm_usage_data_provider_id} + } @{$usage_data_providers}; + my $provider_name = $provider_object[0]->{name}; + + # Split data objects into metric_types i.e. one table row per metric_type + for my $metric_type (@$metric_types) { + my $statistics = $data_object->{'erm_usage_muses'}; + my @filtered_statistics = + grep { $metric_type eq $_->{metric_type} } @$statistics; + my @usage_counts = + map { $_->{usage_count} } @filtered_statistics; + my $sum = scalar(@usage_counts) > 0 + ? eval join '+', @usage_counts + : 0; + + my $data_object_hash = _get_object_hash( + { + data_type => $data_type, + data_object => $data_object, + statistics => \@filtered_statistics, + provider => $provider_name, + metric_type => $metric_type, + period => 'monthly', + sum => $sum + } + ); + + push @report_data, $data_object_hash; + } + } + + return $c->render( status => 200, openapi => \@report_data ); + } + catch { + $c->unhandled_exception($_); + }; + +} + +=head3 yearly_report + +An endpoint to fetch filtered yearly report data for ERM usage statistics + +=cut + +sub yearly_report { + my $c = shift->openapi->valid_input or return; + + return try { + my $args = $c->validation->output; + + my @query_params_array; + my $json = JSON->new; + + if ( ref( $args->{q} ) eq 'ARRAY' ) { + foreach my $q ( @{ $args->{q} } ) { + push @query_params_array, $json->decode($q) + if $q; + } + } + + my $data_type = $c->validation->param('data_type'); + my $data_set = _get_data_set($data_type); + my $data = $c->objects->search($data_set); + + my $usage_data_providers = + Koha::ERM::UsageDataProviders->search( {}, {} )->unblessed; + +# Titles with no data in the selected range will not be returned by the API - we still want to include them if they have been requested + my $requested_ids = + _get_correct_query_param( $data_type, \@query_params_array, + 'yearly' ); + for my $id ( @{$requested_ids} ) { + my $missing_result = _get_result_with_no_statistics( + { + data => $data, + data_type => $data_type, + id => $id, + } + ); + push @{$data}, $missing_result if $missing_result; + } + + my $metric_types = + $query_params_array[0]->{'erm_usage_yuses.metric_type'}; + my @report_data; + + for my $data_object ( @{$data} ) { + + # Add provider name rather than embed provider object + my $usage_data_provider_id = $data_object->{usage_data_provider_id}; + my @provider_object = grep { + $usage_data_provider_id eq $_->{erm_usage_data_provider_id} + } @{$usage_data_providers}; + my $provider_name = $provider_object[0]->{name}; + + # Split data objects into metric_types i.e. one table row per metric_type + for my $metric_type (@$metric_types) { + my $statistics = $data_object->{'erm_usage_yuses'}; + my @filtered_statistics = + grep { $metric_type eq $_->{metric_type} } @$statistics; + + my $data_object_hash = _get_object_hash( + { + data_type => $data_type, + data_object => $data_object, + statistics => \@filtered_statistics, + provider => $provider_name, + metric_type => $metric_type, + period => 'yearly' + } + ); + + push @report_data, $data_object_hash; + } + } + + return $c->render( status => 200, openapi => \@report_data ); + } + catch { + $c->unhandled_exception($_); + }; + +} + +=head3 metric_types_report + +An endpoint to fetch report data for ERM usage statistics based on metric type columns + +=cut + +sub metric_types_report { + my $c = shift->openapi->valid_input or return; + + return try { + + my $args = $c->validation->output; + + my @query_params_array; + my $json = JSON->new; + + if ( ref( $args->{q} ) eq 'ARRAY' ) { + foreach my $q ( @{ $args->{q} } ) { + push @query_params_array, $json->decode($q) + if $q; + } + } + + my $data_type = $c->validation->param('data_type'); + my $data_set = _get_data_set($data_type); + my $data = $c->objects->search($data_set); + + my $usage_data_providers = + Koha::ERM::UsageDataProviders->search( {}, {} )->unblessed; + +# Objects with no data in the selected range will not be returned by the API - we still want to include them if they have been requested + my $requested_ids = + _get_correct_query_param( $data_type, \@query_params_array, + 'monthly' ); + for my $id ( @{$requested_ids} ) { + my $missing_result = _get_result_with_no_statistics( + { + data => $data, + data_type => $data_type, + id => $id, + } + ); + push @{$data}, $missing_result if $missing_result; + } + + my @report_data; + + for my $data_object ( @{$data} ) { + + # Add provider name rather than embed provider object + my $usage_data_provider_id = $data_object->{usage_data_provider_id}; + my @provider_object = grep { + $usage_data_provider_id eq $_->{erm_usage_data_provider_id} + } @{$usage_data_providers}; + my $provider_name = $provider_object[0]->{name}; + $data_object->{provider_name} = $provider_name; + + push @report_data, $data_object; + } + return $c->render( status => 200, openapi => \@report_data ); + } + catch { + $c->unhandled_exception($_); + }; + +} + +=head3 provider_rollup_report + +=cut + +sub provider_rollup_report { + my $c = shift->openapi->valid_input or return; + + return try { + + my $args = $c->validation->output; + + my $usage_data_providers_set = Koha::ERM::UsageDataProviders->new; + my $usage_data_providers = + $c->objects->search($usage_data_providers_set); + + my @query_params_array; + my $json = JSON->new; + + if ( ref( $args->{q} ) eq 'ARRAY' ) { + foreach my $q ( @{ $args->{q} } ) { + push @query_params_array, $json->decode($q) + if $q; + } + } + + my $data_type = $c->validation->param('data_type'); + my $key = 'erm_usage_' . $data_type . 's'; + my $metric_types = + $query_params_array[0][0]->{ $key . '.erm_usage_muses.metric_type' }; + + my @usage_data_provider_report_data; + + for my $usage_data_provider ( @{$usage_data_providers} ) { + +# Split usage_data_providers into metric_types i.e. one table row per metric_type + for my $metric_type (@$metric_types) { + my @filtered_object_data; + + for my $data_object ( @{ $usage_data_provider->{$key} } ) { + my $statistics = $data_object->{'erm_usage_muses'}; + my @filtered_statistics = + grep { $metric_type eq $_->{metric_type} } @$statistics; + my @usage_counts = + map { $_->{usage_count} } @filtered_statistics; + my $sum = scalar(@usage_counts) > 0 + ? eval join '+', @usage_counts + : 0; + + my $data_object_hash = _get_object_hash( + { + data_type => $data_type, + data_object => $data_object, + statistics => \@filtered_statistics, + provider => '', + metric_type => $metric_type, + period => 'monthly', + sum => $sum, + } + ); + + push @filtered_object_data, $data_object_hash; + } + + my @data_object_usage_totals = + map { $_->{usage_total} } @filtered_object_data; + my $provider_rollup_total = + scalar(@data_object_usage_totals) > 0 + ? eval join '+', @data_object_usage_totals + : 0; + + my %usage_data_provider_hash = ( + erm_usage_data_provider_id => + $usage_data_provider->{erm_usage_data_provider_id}, + aggregator => $usage_data_provider->{aggregator}, + api_key => $usage_data_provider->{api_key}, + begin_date => $usage_data_provider->{begin_date}, + customer_id => $usage_data_provider->{customer_id}, + description => $usage_data_provider->{description}, + end_date => $usage_data_provider->{end_date}, + method => $usage_data_provider->{method}, + name => $usage_data_provider->{name}, + report_release => $usage_data_provider->{report_release}, + report_types => $usage_data_provider->{report_types}, + requestor_email => $usage_data_provider->{requestor_email}, + requestor_id => $usage_data_provider->{requestor_id}, + requestor_name => $usage_data_provider->{requestor_name}, + service_type => $usage_data_provider->{service_type}, + service_url => $usage_data_provider->{service_url}, + metric_type => $metric_type, + provider_rollup_total => $provider_rollup_total, + ); + $usage_data_provider_hash{$key} = \@filtered_object_data; + + push @usage_data_provider_report_data, + \%usage_data_provider_hash; + } + } + + return $c->render( + status => 200, + openapi => \@usage_data_provider_report_data + ); + } + catch { + $c->unhandled_exception($_); + }; +} + + + +sub _get_data_set { + my ($data_type) = @_; + + if ( $data_type eq 'title' ) { + return Koha::ERM::UsageTitles->new; + } + if ( $data_type eq 'platform' ) { + return Koha::ERM::UsagePlatforms->new; + } + if ( $data_type eq 'item' ) { + return Koha::ERM::UsageItems->new; + } + if ( $data_type eq 'database' ) { + return Koha::ERM::UsageDatabases->new; + } + return 0; +} + +sub _get_correct_query_param { + my ( $data_type, $array_ref, $period ) = @_; + + my $param; + my @query_params = @$array_ref; + + my $prefix = $period eq 'monthly' ? 'erm_usage_muses' : 'erm_usage_yuses'; + my $key = $prefix . "." . $data_type . "_id"; + + if ( $period eq 'monthly' ) { + $param = $query_params[0][0]->{$key}; + } + else { + $param = $query_params[0]->{$key}; + } + + return $param; +} + +sub _get_result_with_no_statistics { + my ($args) = @_; + + my $data = $args->{data}; + my $data_type = $args->{data_type}; + my $id = $args->{id}; + my $identifier = $data_type . "_id"; + + my $check_result = grep { $id eq $_->{$identifier} } @{$data}; + if ( !$check_result ) { + my $missing_result = _get_missing_data( $data_type, $id ); + my @blank_statistics = (); + return _get_object_hash({ + data_type => $data_type, + data_object => $missing_result, + statistics => \@blank_statistics, + provider => '', + metric_type => '', + }); + } + return 0; +} + +=head3 _get_object_hash + +Returns a hash for a given data type with some additional parameters. + +=cut + +sub _get_object_hash { + my ($args) = @_; + + my $data_type = $args->{data_type}; + my $data_object = $args->{data_object}; + my $statistics = $args->{statistics}; + my $provider = $args->{provider}; + my $metric_type = $args->{metric_type}; + my $period = $args->{period}; + my %object_hash; + + if ( $data_type eq 'title' ) { + %object_hash = ( + usage_data_provider_id => $data_object->{usage_data_provider_id}, + provider_name => $provider, + title_id => $data_object->{title_id}, + title => $data_object->{title}, + online_issn => $data_object->{online_issn}, + print_issn => $data_object->{print_issn}, + title_doi => $data_object->{title_doi}, + title_uri => $data_object->{title_uri}, + publisher => $data_object->{publisher}, + publisher_id => $data_object->{publisher_id}, + metric_type => $metric_type, + ); + } + if ( $data_type eq 'platform' ) { + %object_hash = ( + usage_data_provider_id => $data_object->{usage_data_provider_id}, + provider_name => $provider, + metric_type => $metric_type, + platform => $data_object->{platform}, + platform_id => $data_object->{platform_id}, + ); + } + if ( $data_type eq 'database' ) { + %object_hash = ( + usage_data_provider_id => $data_object->{usage_data_provider_id}, + provider_name => $provider, + database_id => $data_object->{database_id}, + database => $data_object->{database}, + platform => $data_object->{platform}, + publisher => $data_object->{publisher}, + publisher_id => $data_object->{publisher_id}, + metric_type => $metric_type, + ); + } + if ( $data_type eq 'item' ) { + %object_hash = ( + usage_data_provider_id => $data_object->{usage_data_provider_id}, + provider_name => $provider, + item_id => $data_object->{item_id}, + item => $data_object->{item}, + platform => $data_object->{platform}, + publisher => $data_object->{publisher}, + publisher_id => $data_object->{publisher_id}, + metric_type => $metric_type, + ); + } + $object_hash{usage_total} = $sum if $sum; + + if ( $period eq 'yearly' ) { + $object_hash{erm_usage_yuses} = $statistics; + } + else { + $object_hash{erm_usage_muses} = $statistics; + } + return \%object_hash; +} + +sub _get_missing_data { + my ( $data_type, $id ) = @_; + + my $item; + + if ( $data_type eq 'title' ) { + $item = + Koha::ERM::UsageTitles->find( { title_id => $id }, {} )->unblessed; + } + if ( $data_type eq 'platform' ) { + $item = Koha::ERM::UsagePlatforms->find( { platform_id => $id }, {} ) + ->unblessed; + } + if ( $data_type eq 'database' ) { + $item = Koha::ERM::UsageDatabases->find( { database_id => $id }, {} ) + ->unblessed; + } + if ( $data_type eq 'item' ) { + $item = + Koha::ERM::UsageItems->find( { item_id => $id }, {} )->unblessed; + } + + return $item if $item; +} + +1; diff --git a/Koha/REST/V1/ERM/UsageDataProviders.pm b/Koha/REST/V1/ERM/UsageDataProviders.pm index be53957831..8aa9e71542 100644 --- a/Koha/REST/V1/ERM/UsageDataProviders.pm +++ b/Koha/REST/V1/ERM/UsageDataProviders.pm @@ -359,92 +359,4 @@ sub test_connection { }; } -=head3 providers_report - -=cut - -sub providers_report { - my $c = shift->openapi->valid_input or return; - - return try { - - my $args = $c->validation->output; - - my $usage_data_providers_set = Koha::ERM::UsageDataProviders->new; - my $usage_data_providers = $c->objects->search( $usage_data_providers_set ); - - my @query_params_array; - my $json = JSON->new; - - if ( ref( $args->{q} ) eq 'ARRAY' ) { - foreach my $q ( @{ $args->{q} } ) { - push @query_params_array, $json->decode($q) - if $q; - } - } - - my $metric_types = $query_params_array[0][0]->{'erm_usage_titles.erm_usage_muses.metric_type'}; - - my @usage_data_provider_report_data; - - for my $usage_data_provider ( @{ $usage_data_providers } ) { - - # Split usage_data_providers into metric_types i.e. one table row per metric_type - for my $metric_type ( @$metric_types ) { - my @filtered_title_data; - - for my $title ( @{ $usage_data_provider->{'erm_usage_titles'} }) { - my $statistics = $title->{'erm_usage_muses'}; - my @filtered_statistics = grep { $metric_type eq $_->{metric_type} } @$statistics; - - my %title_hash = ( - usage_data_provider_id => $title->{usage_data_provider_id}, - title_id => $title->{title_id}, - title => $title->{title}, - erm_usage_muses => \@filtered_statistics, - online_issn => $title->{online_issn}, - print_issn => $title->{print_issn}, - title_doi => $title->{title_doi}, - title_uri => $title->{title_uri}, - metric_type => $metric_type, - publisher => $title->{publisher}, - publisher_id => $title->{publisher_id}, - ); - - push @filtered_title_data, \%title_hash; - } - - - my %usage_data_provider_hash = ( - erm_usage_data_provider_id => $usage_data_provider->{erm_usage_data_provider_id}, - erm_usage_titles => \@filtered_title_data, - aggregator => $usage_data_provider->{aggregator}, - api_key => $usage_data_provider->{api_key}, - begin_date => $usage_data_provider->{begin_date}, - customer_id => $usage_data_provider->{customer_id}, - description => $usage_data_provider->{description}, - end_date => $usage_data_provider->{end_date}, - method => $usage_data_provider->{method}, - name => $usage_data_provider->{name}, - report_release => $usage_data_provider->{report_release}, - report_types => $usage_data_provider->{report_types}, - requestor_email => $usage_data_provider->{requestor_email}, - requestor_id => $usage_data_provider->{requestor_id}, - requestor_name => $usage_data_provider->{requestor_name}, - service_type => $usage_data_provider->{service_type}, - service_url => $usage_data_provider->{service_url}, - metric_type => $metric_type - ); - - push @usage_data_provider_report_data, \%usage_data_provider_hash; - }; - }; - - return $c->render( status => 200, openapi => \@usage_data_provider_report_data ); - } - catch { - $c->unhandled_exception($_); - }; -} - 1; diff --git a/Koha/REST/V1/ERM/UsageTitles.pm b/Koha/REST/V1/ERM/UsageTitles.pm index b246364364..b32493257b 100644 --- a/Koha/REST/V1/ERM/UsageTitles.pm +++ b/Koha/REST/V1/ERM/UsageTitles.pm @@ -18,8 +18,12 @@ package Koha::REST::V1::ERM::UsageTitles; use Modern::Perl; use Mojo::Base 'Mojolicious::Controller'; +use Module::Load qw( load ); use Koha::ERM::UsageTitles; +use Koha::ERM::UsagePlatforms; +use Koha::ERM::UsageItems; +use Koha::ERM::UsageDatabases; use Koha::ERM::UsageDataProvider; use Koha::ERM::UsageDataProviders; diff --git a/api/v1/swagger/definitions/erm_custom_report.yaml b/api/v1/swagger/definitions/erm_custom_report.yaml new file mode 100644 index 0000000000..8193f74d82 --- /dev/null +++ b/api/v1/swagger/definitions/erm_custom_report.yaml @@ -0,0 +1,64 @@ +type: object +properties: + title_id: + type: integer + description: internally assigned title identifier + readOnly: true + title: + description: title of the title + type: + - string + - "null" + usage_data_provider_id: + description: usage_data_provider the title is harvested by + type: integer + title_doi: + description: doi number of the title + type: + - string + - "null" + print_issn: + description: print_issn number of the title + type: + - string + - "null" + online_issn: + description: online_issn number of the title + type: + - string + - "null" + title_uri: + description: title_uri number of the title + type: + - string + - "null" + provider_name: + description: provider_name of the title + type: + - string + - "null" + metric_type: + description: metric_type of the title data + type: + - string + - "null" + publisher: + description: publisher of the title data + type: + - string + - "null" + publisher_id: + description: publisher_id of the title data + type: + - string + - "null" + erm_usage_muses: + type: array + description: usage mus + items: + $ref: erm_usage_mus.yaml + erm_usage_yuses: + type: array + description: usage yus + items: + $ref: erm_usage_yus.yaml \ No newline at end of file diff --git a/api/v1/swagger/paths/erm_custom_reports.yaml b/api/v1/swagger/paths/erm_custom_reports.yaml new file mode 100644 index 0000000000..37da5cf475 --- /dev/null +++ b/api/v1/swagger/paths/erm_custom_reports.yaml @@ -0,0 +1,243 @@ +"/erm/eUsage/monthly_report/{data_type}": + get: + x-mojo-to: ERM::CustomReports#monthly_report + operationId: getERMUsageMonthlyReport + tags: + - usage_report + summary: get usage monthly report + produces: + - application/json + parameters: + - name: x-koha-embed + in: header + required: false + description: Embed list sent as a request header + type: array + items: + type: string + enum: + - erm_usage_muses + collectionFormat: csv + - $ref: "../swagger.yaml#/parameters/data_type_pp" + - $ref: "../swagger.yaml#/parameters/match" + - $ref: "../swagger.yaml#/parameters/order_by" + - $ref: "../swagger.yaml#/parameters/page" + - $ref: "../swagger.yaml#/parameters/per_page" + - $ref: "../swagger.yaml#/parameters/q_param" + - $ref: "../swagger.yaml#/parameters/q_body" + - $ref: "../swagger.yaml#/parameters/q_header" + - $ref: "../swagger.yaml#/parameters/request_id_header" + responses: + 200: + description: custom_report + schema: + items: + $ref: "../swagger.yaml#/definitions/erm_custom_report" + 401: + description: authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + 403: + description: access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + 404: + description: ressource not found + schema: + $ref: "../swagger.yaml#/definitions/error" + 500: + description: |- + internal server error. possible `error_code` attribute values: + * `internal_server_error` + schema: + $ref: "../swagger.yaml#/definitions/error" + 503: + description: under maintenance + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + erm: 1 +"/erm/eUsage/yearly_report/{data_type}": + get: + x-mojo-to: ERM::CustomReports#yearly_report + operationId: getERMUsageYearlyReport + tags: + - usage_report + summary: get usage yearly report + produces: + - application/json + parameters: + - name: x-koha-embed + in: header + required: false + description: Embed list sent as a request header + type: array + items: + type: string + enum: + - erm_usage_yuses + collectionFormat: csv + - $ref: "../swagger.yaml#/parameters/data_type_pp" + - $ref: "../swagger.yaml#/parameters/match" + - $ref: "../swagger.yaml#/parameters/order_by" + - $ref: "../swagger.yaml#/parameters/page" + - $ref: "../swagger.yaml#/parameters/per_page" + - $ref: "../swagger.yaml#/parameters/q_param" + - $ref: "../swagger.yaml#/parameters/q_body" + - $ref: "../swagger.yaml#/parameters/q_header" + - $ref: "../swagger.yaml#/parameters/request_id_header" + responses: + 200: + description: custom_report + schema: + items: + $ref: "../swagger.yaml#/definitions/erm_custom_report" + 401: + description: authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + 403: + description: access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + 404: + description: ressource not found + schema: + $ref: "../swagger.yaml#/definitions/error" + 500: + description: |- + internal server error. possible `error_code` attribute values: + * `internal_server_error` + schema: + $ref: "../swagger.yaml#/definitions/error" + 503: + description: under maintenance + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + erm: 1 +"/erm/eUsage/metric_types_report/{data_type}": + get: + x-mojo-to: ERM::CustomReports#metric_types_report + operationId: getERMUsageMetricTypesReport + tags: + - usage_report + summary: get usage metric_types report + produces: + - application/json + parameters: + - name: x-koha-embed + in: header + required: false + description: Embed list sent as a request header + type: array + items: + type: string + enum: + - erm_usage_muses + collectionFormat: csv + - $ref: "../swagger.yaml#/parameters/data_type_pp" + - $ref: "../swagger.yaml#/parameters/match" + - $ref: "../swagger.yaml#/parameters/order_by" + - $ref: "../swagger.yaml#/parameters/page" + - $ref: "../swagger.yaml#/parameters/per_page" + - $ref: "../swagger.yaml#/parameters/q_param" + - $ref: "../swagger.yaml#/parameters/q_body" + - $ref: "../swagger.yaml#/parameters/q_header" + - $ref: "../swagger.yaml#/parameters/request_id_header" + responses: + 200: + description: custom_report + schema: + items: + $ref: "../swagger.yaml#/definitions/erm_custom_report" + 401: + description: authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + 403: + description: access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + 404: + description: ressource not found + schema: + $ref: "../swagger.yaml#/definitions/error" + 500: + description: |- + internal server error. possible `error_code` attribute values: + * `internal_server_error` + schema: + $ref: "../swagger.yaml#/definitions/error" + 503: + description: under maintenance + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + erm: 1 +"/erm/eUsage/provider_rollup_report/{data_type}": + get: + x-mojo-to: ERM::CustomReports#provider_rollup_report + operationId: getERMUsageProviderRollupReport + tags: + - usage_report + summary: get usage provider rollup report + produces: + - application/json + parameters: + - name: x-koha-embed + in: header + required: false + description: Embed list sent as a request header + type: array + items: + type: string + enum: + - erm_usage_titles.erm_usage_muses + - erm_usage_platforms.erm_usage_muses + - erm_usage_databases.erm_usage_muses + - erm_usage_items.erm_usage_muses + collectionFormat: csv + - $ref: "../swagger.yaml#/parameters/data_type_pp" + - $ref: "../swagger.yaml#/parameters/match" + - $ref: "../swagger.yaml#/parameters/order_by" + - $ref: "../swagger.yaml#/parameters/page" + - $ref: "../swagger.yaml#/parameters/per_page" + - $ref: "../swagger.yaml#/parameters/q_param" + - $ref: "../swagger.yaml#/parameters/q_body" + - $ref: "../swagger.yaml#/parameters/q_header" + - $ref: "../swagger.yaml#/parameters/request_id_header" + responses: + 200: + description: custom_report + schema: + items: + $ref: "../swagger.yaml#/definitions/erm_custom_report" + 401: + description: authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + 403: + description: access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + 404: + description: ressource not found + schema: + $ref: "../swagger.yaml#/definitions/error" + 500: + description: |- + internal server error. possible `error_code` attribute values: + * `internal_server_error` + schema: + $ref: "../swagger.yaml#/definitions/error" + 503: + description: under maintenance + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + erm: 1 \ No newline at end of file diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 72d287cc10..db68b9030a 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -44,6 +44,8 @@ definitions: $ref: ./definitions/erm_counter_file.yaml erm_counter_log: $ref: ./definitions/erm_counter_log.yaml + erm_custom_report: + $ref: ./definitions/erm_custom_report.yaml erm_default_usage_report: $ref: ./definitions/erm_default_usage_report.yaml erm_eholdings_title: @@ -285,6 +287,14 @@ paths: $ref: "./paths/erm_eholdings_packages.yaml#/~1erm~1eholdings~1{provider}~1packages~1{package_id}" "/erm/eholdings/{provider}/packages/{package_id}/resources": $ref: "./paths/erm_eholdings_packages_resources.yaml#/~1erm~1eholdings~1{provider}~1packages~1{package_id}~1resources" + "/erm/eUsage/monthly_report/{data_type}": + $ref: "./paths/erm_custom_reports.yaml#/~1erm~1eUsage~1monthly_report~1{data_type}" + "/erm/eUsage/yearly_report/{data_type}": + $ref: "./paths/erm_custom_reports.yaml#/~1erm~1eUsage~1yearly_report~1{data_type}" + "/erm/eUsage/metric_types_report/{data_type}": + $ref: "./paths/erm_custom_reports.yaml#/~1erm~1eUsage~1metric_types_report~1{data_type}" + "/erm/eUsage/provider_rollup_report/{data_type}": + $ref: "./paths/erm_custom_reports.yaml#/~1erm~1eUsage~1provider_rollup_report~1{data_type}" /erm/licenses: $ref: ./paths/erm_licenses.yaml#/~1erm~1licenses "/erm/licenses/{license_id}": @@ -297,8 +307,6 @@ paths: $ref: "./paths/erm_usage_data_providers.yaml#/~1erm~1usage_data_providers~1{erm_usage_data_provider_id}~1run" "/erm/usage_data_providers/{erm_usage_data_provider_id}/test_connection": $ref: "./paths/erm_usage_data_providers.yaml#/~1erm~1usage_data_providers~1{erm_usage_data_provider_id}~1test_connection" - "/erm/usage_data_providers/report": - $ref: "./paths/erm_usage_data_providers.yaml#/~1erm~1usage_data_providers~1report" /erm/usage_titles: $ref: ./paths/erm_usage_titles.yaml#/~1erm~1usage_titles /erm/users: diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportBuilder.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportBuilder.vue index b014852853..c86fe7df57 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportBuilder.vue +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportBuilder.vue @@ -580,21 +580,27 @@ export default { this.time_period_columns_builder = time_period_columns }, - buildMonthlyUrlQuery(query, time_period_columns, data_display) { + buildMonthlyUrlQuery( + query, + time_period_columns, + data_display, + db_table + ) { let url let prefix switch (data_display) { case "monthly": - url = "/api/v1/erm/usage_titles/monthly_report" + case "monthly_with_totals": + url = `/api/v1/erm/eUsage/monthly_report/${db_table}` prefix = "erm_usage_muses" break case "metric_type": - url = "/api/v1/erm/usage_titles/metric_types_report" + url = `/api/v1/erm/eUsage/metric_types_report/${db_table}` prefix = "erm_usage_muses" break - case "usage_data_provider": - url = "/api/v1/erm/usage_data_providers/report" - prefix = "erm_usage_titles.erm_usage_muses" + case "usage_data_provider": // TODO: platform/item/database table embeds? + url = `/api/v1/erm/eUsage/provider_rollup_report/${db_table}` + prefix = `erm_usage_${db_table}s.erm_usage_muses` break } @@ -656,8 +662,8 @@ export default { return url }, - buildYearlyUrlQuery(query) { - let url = "/api/v1/erm/usage_titles/yearly_report" + buildYearlyUrlQuery(query, db_table) { + let url = `/api/v1/erm/eUsage/yearly_report/${db_table}` const { start_year, end_year, @@ -846,13 +852,31 @@ export default { queryObject.metric_types = final_metric_types } + // Determine which database table should be queried + let db_table + switch (report_type.substring(0, 1)) { + case "P": + db_table = "platform" + break + case "T": + db_table = "title" + break + case "I": + db_table = "item" + break + case "D": + db_table = "database" + break + } + const url = !data_display.includes("yearly") ? this.buildMonthlyUrlQuery( queryObject, this.time_period_columns_builder, - data_display + data_display, + db_table ) - : this.buildYearlyUrlQuery(queryObject) + : this.buildYearlyUrlQuery(queryObject, db_table) const type = data_display const columns = data_display === "usage_data_provider" diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportsViewer.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportsViewer.vue index c953e46bd0..608a8729ae 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportsViewer.vue +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/UsageStatisticsReportsViewer.vue @@ -42,14 +42,30 @@ export default { beforeCreate() { const urlParams = JSON.parse(this.$route.query.data) this.params = urlParams - this.report_type = this.params.type + let data_type + switch (this.params.queryObject.report_type.substring(0, 1)) { + case "P": + data_type = "platform" + break + case "T": + data_type = "title" + break + case "I": + data_type = "item" + break + case "D": + data_type = "database" + break + } + this.data_type = data_type this.embed = this.report_type === "yearly" ? ["erm_usage_yuses"] : ["erm_usage_muses"] switch (this.report_type) { case "monthly": + case "monthly_with_totals": this.embed = "erm_usage_muses" break case "yearly": @@ -59,7 +75,7 @@ export default { this.embed = "erm_usage_muses" break case "usage_data_provider": - this.embed = "erm_usage_titles.erm_usage_muses" + this.embed = `erm_usage_${data_type}s.erm_usage_muses` break } @@ -71,7 +87,11 @@ export default { building_table: false, initialized: false, tableOptions: { - columns: this.buildColumnArray(this.report_type, this.params), + columns: this.buildColumnArray( + this.report_type, + this.params, + this.data_type + ), options: { embed: this.embed }, url: () => this.tableURL(this.year, this.params), table_settings: this.report_type.includes("monthly") @@ -86,7 +106,7 @@ export default { } }, methods: { - buildColumnArray(report_type, params) { + buildColumnArray(report_type, params, data_type) { const columns = params.columns const months_data = this.getMonthsData() const column_options = this.getColumnOptions() @@ -103,8 +123,10 @@ export default { report_type !== "usage_data_provider" && column_set.unshift({ - title: __("Title"), - data: "title", + title: __( + data_type.charAt(0).toUpperCase() + data_type.slice(1) + ), + data: data_type, searchable: true, orderable: true, }) @@ -131,15 +153,15 @@ export default { column_set.push({ title: __("Period Total"), render: function (data, type, row, meta) { - const sum = row.erm_usage_titles.reduce( - (acc, title) => { - const titleSum = title.erm_usage_muses.reduce( + const sum = row[`erm_usage_${data_type}s`].reduce( + (acc, obj) => { + const objSum = obj.erm_usage_muses.reduce( (acc, mus) => { return acc + mus.usage_count }, 0 ) - return acc + titleSum + return acc + objSum }, 0 ) @@ -275,10 +297,25 @@ export default { this.$refs.table.redraw(this.tableURL(this.year, this.params)) }, buildFilteredQuery(query, time_period_columns, year) { - let url = "/api/v1/erm/usage_titles/monthly_report" const queryObject = {} const { metric_types, usage_data_providers, titles, report_type } = query + let data_type + switch (report_type.substring(0, 1)) { + case "P": + data_type = "platform" + break + case "T": + data_type = "title" + break + case "I": + data_type = "item" + break + case "D": + data_type = "database" + break + } + let url = `/api/v1/erm/eUsage/monthly_report/${data_type}` // Identify the year queryObject[`erm_usage_muses.year`] = year -- 2.39.5