Bug 34587: Add reports by data provider
[koha.git] / Koha / ERM / UsageDataProvider.pm
1 package Koha::ERM::UsageDataProvider;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use HTTP::Request;
21 use JSON qw( decode_json );
22 use LWP::UserAgent;
23 use Text::CSV_XS qw( csv );
24
25 use Koha::Exceptions;
26
27 use base qw(Koha::Object);
28
29 use Koha::ERM::CounterFile;
30 use Koha::ERM::CounterFiles;
31 use Koha::BackgroundJob::ErmSushiHarvester;
32
33 =head1 NAME
34
35 Koha::ERM::UsageDataProvider - Koha ErmUsageDataProvider Object class
36
37 =head1 API
38
39 =head2 Class Methods
40
41 =head3 counter_files
42
43 Getter/setter for counter_files for this usage data provider
44
45 =cut
46
47 sub counter_files {
48     my ( $self, $counter_files ) = @_;
49
50     if ($counter_files) {
51         for my $counter_file (@$counter_files) {
52             Koha::ERM::CounterFile->new($counter_file)
53               ->store( $self->{job_callbacks} );
54         }
55     }
56     my $counter_files_rs = $self->_result->erm_counter_files;
57     return Koha::ERM::CounterFiles->_new_from_dbic($counter_files_rs);
58 }
59
60 =head3 enqueue_counter_file_processing_job
61
62 Enqueues a background job to process a COUNTER file that has been uploaded
63
64 =cut
65
66 sub enqueue_counter_file_processing_job {
67     my ( $self, $args ) = @_;
68
69     my @jobs;
70     my $job_id = Koha::BackgroundJob::ErmSushiHarvester->new->enqueue(
71         {
72             ud_provider_id => $self->erm_usage_data_provider_id,
73             file_content   => $args->{file_content},
74         }
75     );
76
77     push(
78         @jobs,
79         {
80             job_id => $job_id
81         }
82     );
83
84     return \@jobs;
85 }
86
87 =head3 enqueue_sushi_harvest_jobs
88
89 Enqueues one harvest background job for each report type in this usage data provider
90
91 =cut
92
93 sub enqueue_sushi_harvest_jobs {
94     my ( $self, $args ) = @_;
95
96     my @report_types = split( /;/, $self->report_types );
97
98     my @jobs;
99     foreach my $report_type (@report_types) {
100
101         my $job_id = Koha::BackgroundJob::ErmSushiHarvester->new->enqueue(
102             {
103                 ud_provider_id => $self->erm_usage_data_provider_id,
104                 report_type    => $report_type
105             }
106         );
107
108         push(
109             @jobs,
110             {
111                 report_type => $report_type,
112                 job_id      => $job_id
113             }
114         );
115     }
116
117     return \@jobs;
118 }
119
120 =head3 harvest
121
122     $ud_provider->harvest(
123         {
124             step_callback        => sub { $self->step; },
125             set_size_callback    => sub { $self->set_job_size(@_); },
126             add_message_callback => sub { $self->add_message(@_); },
127         }
128     );
129
130 Run the SUSHI harvester of this usage data provider
131 Builds the URL query and requests the COUNTER 5 SUSHI service
132
133 COUNTER SUSHI api spec:
134 https://app.swaggerhub.com/apis/COUNTER/counter-sushi_5_0_api/5.0.2
135
136 =over
137
138 =item report_type
139
140 Report type to run this harvest on
141
142 =back
143
144 =over
145
146 =item background_job_callbacks
147
148 Receive background_job_callbacks to be able to update job
149
150 =back
151
152 =cut
153
154 sub harvest {
155     my ( $self, $report_type, $background_job_callbacks ) = @_;
156
157     # Set class wide vars
158     $self->{job_callbacks} = $background_job_callbacks;
159     $self->{report_type} = $report_type;
160
161     my $url      = $self->_build_url_query;
162     my $request  = HTTP::Request->new( 'GET' => $url );
163     my $ua       = LWP::UserAgent->new;
164     my $response = $ua->simple_request($request);
165
166     if ( $response->code >= 400 ) {
167         my $result = decode_json( $response->decoded_content );
168
169         my $message;
170         if ( ref($result) eq 'ARRAY' ) {
171             for my $r (@$result) {
172                 $message .= $r->{message};
173             }
174         }
175         else {
176             #TODO: May want to check $result->{Report_Header}->{Exceptions} here
177             $message = $result->{message} || $result->{Message} || q{};
178             if ( $result->{errors} ) {
179                 for my $e ( @{ $result->{errors} } ) {
180                     $message .= $e->{message};
181                 }
182             }
183         }
184
185         #TODO: May want to add a job error message here?
186         warn sprintf "ERROR - SUSHI service %s returned %s - %s\n", $url,
187           $response->code, $message;
188         if ( $response->code == 404 ) {
189             Koha::Exceptions::ObjectNotFound->throw($message);
190         }
191         elsif ( $response->code == 401 ) {
192             Koha::Exceptions::Authorization::Unauthorized->throw($message);
193         }
194         else {
195             #TODO: May want to add a job error message here?
196             die sprintf "ERROR requesting SUSHI service\n%s\ncode %s: %s\n",
197               $url, $response->code,
198               $message;
199         }
200     }
201     elsif ( $response->code == 204 ) {    # No content
202         return;
203     }
204
205     # Parse the SUSHI response
206     $self->parse_SUSHI_response( decode_json( $response->decoded_content ) );
207 }
208
209 =head3 parse_SUSHI_response
210
211     $self->parse_SUSHI_response( decode_json( $response->decoded_content ) );
212
213 Parse the SUSHI response, prepare the COUNTER report file header,
214 column headings and body
215
216 =over
217
218 =item result
219
220 The result of the SUSHI response after json decoded
221
222 =back
223
224 =cut
225
226 sub parse_SUSHI_response {
227     my ( $self, $result ) = @_;
228
229     # Set class wide sushi response content
230     $self->{sushi} = {
231         header => $result->{Report_Header},
232         body   => $result->{Report_Items}
233     };
234
235     #TODO: Handle empty $self->{sushi}->{body} here!
236
237     # Get ready to build COUNTER file
238     my @report_header          = $self->_COUNTER_report_header;
239     my @report_column_headings = $self->_COUNTER_report_column_headings;
240     my @report_body            = $self->_COUNTER_report_body;
241
242     $self->_build_COUNTER_report_file( \@report_header,
243         \@report_column_headings, \@report_body );
244 }
245
246 =head2 Internal methods
247
248 =head3 _build_url_query
249
250 Build the URL query params for COUNTER 5 SUSHI request
251
252 =cut
253
254 sub _build_url_query {
255     my ($self) = @_;
256
257     unless ( $self->service_url && $self->customer_id ) {
258         die sprintf
259 "SUSHI Harvesting config for usage data provider %d is missing service_url or customer_id\n",
260           $self->erm_usage_data_provider_id;
261     }
262
263     # FIXME: service_url needs to end in 'reports/'
264     # below concat will result in a badly formed URL otherwise
265     # Either validate this on UI form, here, or both
266     my $url = $self->service_url;
267
268     $url .= $self->{report_type};
269     $url .= '?customer_id=' . $self->customer_id;
270     $url .= '&requestor_id=' . $self->requestor_id if $self->requestor_id;
271     $url .= '&api_key=' . $self->api_key           if $self->api_key;
272     $url .= '&begin_date=' . $self->begin_date     if $self->begin_date;
273     $url .= '&end_date=' . $self->end_date         if $self->end_date;
274
275     return $url;
276 }
277
278 =head3 _build_COUNTER_report_file
279
280 Build the COUNTER file
281 https://cop5.projectcounter.org/en/5.0.2/03-specifications/02-formats-for-counter-reports.html#report-header
282
283 =cut
284
285 sub _build_COUNTER_report_file {
286     my ( $self, $header, $column_headings, $body ) = @_;
287
288     my @report = ( @{$header}, @{$column_headings}, @{$body} );
289
290     #TODO: change this to tab instead of comma
291     csv( in => \@report, out => \my $counter_file, encoding => "utf-8" );
292
293     $self->counter_files(
294         [
295             {
296                 usage_data_provider_id => $self->erm_usage_data_provider_id,
297                 file_content           => $counter_file,
298                 date_uploaded => POSIX::strftime( "%Y%m%d%H%M%S", localtime ),
299
300                 #TODO: add ".csv" to end of filename here
301                 filename => $self->name . "_" . $self->{report_type},
302                 type  =>  $self->{report_type}
303             }
304         ]
305     );
306 }
307
308 =head3 _COUNTER_report_header
309
310 Return a COUNTER report header
311 https://cop5.projectcounter.org/en/5.0.2/04-reports/03-title-reports.html
312
313 =cut
314
315 sub _COUNTER_report_header {
316     my ($self) = @_;
317
318     my $header = $self->{sushi}->{header};
319
320     my @metric_types_string =
321       $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Metric_Type" );
322
323     my $begin_date =
324       $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Begin_Date" );
325     my $end_date =
326       $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "End_Date" );
327
328     return (
329         [ Report_Name      => $header->{Report_Name}      || "" ],
330         [ Report_ID        => $header->{Report_ID}        || "" ],
331         [ Release          => $header->{Release}          || "" ],
332         [ Institution_Name => $header->{Institution_Name} || "" ],
333         [
334             Institution_ID => join(
335                 "; ",
336                 map( $_->{Type} . ":" . $_->{Value},
337                     @{ $header->{Institution_ID} } )
338               )
339               || ""
340         ],
341         [
342             Metric_Types => join( "; ", split( /\|/, $metric_types_string[0] ) )
343               || ""
344         ],
345         [
346             Report_Filters => join(
347                 "; ",
348                 map( $_->{Name} . ":" . $_->{Value},
349                     @{ $header->{Report_Filters} } )
350               )
351               || ""
352         ],
353
354 #TODO: Report_Attributes may need parsing, test this with a SUSHI response that provides it
355         [ Report_Attributes => $header->{Report_Attributes} || "" ],
356         [
357             Exceptions => join(
358                 "; ",
359                 map( $_->{Code} . ": "
360                       . $_->{Message} . " ("
361                       . $_->{Data} . ")",
362                     @{ $header->{Exceptions} } )
363               )
364               || ""
365         ],
366         [
367                 Reporting_Period => "Begin_Date="
368               . $begin_date
369               . "; End_Date="
370               . $end_date
371         ],
372         [ Created    => $header->{Created}    || "" ],
373         [ Created_By => $header->{Created_By} || "" ],
374         [""]    #empty 13th line
375     );
376 }
377
378 =head3 _COUNTER_item_report_row
379
380 Return a COUNTER item for the COUNTER items report body
381 https://cop5.projectcounter.org/en/5.0.2/04-reports/04-item-reports.html#column-headings-elements
382
383 =cut
384
385 sub _COUNTER_item_report_row {
386     my ( $self, $item_row, $metric_type, $total_usage, $monthly_usages ) = @_;
387
388     return (
389         [
390             $item_row->{Item}      || "",
391             $item_row->{Publisher} || "",
392             $self->_get_SUSHI_Type_Value( $item_row->{Publisher_ID}, "ISNI" )
393               || "",
394             $item_row->{Platform}                                       || "",
395             $self->_get_SUSHI_Type_Value( $item_row->{Item_ID}, "DOI" ) || "",
396             $item_row->{Proprietary_ID}                                 || "",
397             "",    #FIXME: What goes in URI?
398             $metric_type,
399             $total_usage,
400             @{$monthly_usages}
401         ]
402     );
403 }
404
405 =head3 _COUNTER_database_report_row
406
407 Return a COUNTER database for the COUNTER databases report body
408 https://cop5.projectcounter.org/en/5.0.2/04-reports/02-database-reports.html#column-headings-elements
409
410 =cut
411
412 sub _COUNTER_database_report_row {
413     my ( $self, $database_row, $metric_type, $total_usage, $monthly_usages ) =
414       @_;
415
416     return (
417         [
418             $database_row->{Database}  || "",
419             $database_row->{Publisher} || "",
420             $self->_get_SUSHI_Type_Value( $database_row->{Publisher_ID},
421                 "ISNI" )
422               || "",
423             $database_row->{Platform}       || "",
424             $database_row->{Proprietary_ID} || "",
425             $metric_type,
426             $total_usage,
427             @{$monthly_usages}
428         ]
429     );
430 }
431
432 =head3 _COUNTER_platform_report_row
433
434 Return a COUNTER platform for the COUNTER platforms report body
435 https://cop5.projectcounter.org/en/5.0.2/04-reports/01-platform-reports.html#column-headings-elements
436
437 =cut
438
439 sub _COUNTER_platform_report_row {
440     my ( $self, $platform_row, $metric_type, $total_usage, $monthly_usages ) =
441       @_;
442
443     return (
444         [
445             $platform_row->{Platform} || "", $metric_type,
446             $total_usage,                    @{$monthly_usages}
447         ]
448     );
449 }
450
451 =head3 _COUNTER_title_report_row
452
453 Return a COUNTER title for the COUNTER titles report body
454 https://cop5.projectcounter.org/en/5.0.2/04-reports/03-title-reports.html#column-headings-elements
455
456 =cut
457
458 sub _COUNTER_title_report_row {
459     my ( $self, $title_row, $metric_type, $total_usage, $monthly_usages ) = @_;
460
461     my $header = $self->{sushi}->{header};
462     my $specific_fields =
463       $self->get_report_type_specific_fields( $header->{Report_ID} );
464
465     return (
466         [
467             # Title
468             $title_row->{Title} || "",
469
470             # Publisher
471             $title_row->{Publisher} || "",
472
473             # Publisher_ID
474             $self->_get_SUSHI_Type_Value( $title_row->{Publisher_ID}, "ISNI" )
475               || "",
476
477             # Platform
478             $title_row->{Platform} || "",
479
480             # DOI
481             $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "DOI" ) || "",
482
483             # Proprietary_ID
484             $self->_get_SUSHI_Type_Value(
485                 $title_row->{Item_ID}, "Proprietary"
486               )
487               || "",
488
489             # ISBN
490             grep ( /ISBN/, @{$specific_fields} )
491             ? ( $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "ISBN" )
492                   || "" )
493             : (),
494
495             # Print_ISSN
496             $self->_get_SUSHI_Type_Value( $title_row->{Item_ID}, "Print_ISSN" )
497               || "",
498
499             # Online_ISSN
500             $self->_get_SUSHI_Type_Value(
501                 $title_row->{Item_ID}, "Online_ISSN"
502               )
503               || "",
504
505             # URI - FIXME: What goes in URI?
506             "",
507
508             # YOP
509             grep ( /YOP/, @{$specific_fields} )
510             ? ( $title_row->{YOP} || "" )
511             : (),
512
513             # Access_Type
514             grep ( /Access_Type/, @{$specific_fields} )
515             ? ( $title_row->{Access_Type} || "" )
516             : (),
517
518             # Metric_Type
519             $metric_type,
520
521             # Report_Period_Total
522             $total_usage,
523
524             # Monthly usage entries
525             @{$monthly_usages}
526         ]
527     );
528 }
529
530 =head3 _COUNTER_report_row
531
532 Return a COUNTER row for the COUNTER report body
533
534 =cut
535
536 sub _COUNTER_report_row {
537     my ( $self, $report_row, $metric_type ) = @_;
538
539     my $header = $self->{sushi}->{header};
540
541     my ( $total_usage, @monthly_usages ) =
542       $self->_get_row_usages( $report_row, $metric_type );
543
544     if ( $header->{Report_ID} =~ /PR/i ) {
545         return $self->_COUNTER_platform_report_row( $report_row, $metric_type,
546             $total_usage, \@monthly_usages );
547     }
548     elsif ( $header->{Report_ID} =~ /DR/i ) {
549         return $self->_COUNTER_database_report_row( $report_row, $metric_type,
550             $total_usage, \@monthly_usages );
551     }
552     elsif ( $header->{Report_ID} =~ /IR/i ) {
553         return $self->_COUNTER_item_report_row( $report_row, $metric_type,
554             $total_usage, \@monthly_usages );
555     }
556     elsif ( $header->{Report_ID} =~ /TR/i ) {
557         return $self->_COUNTER_title_report_row( $report_row, $metric_type,
558             $total_usage, \@monthly_usages );
559     }
560 }
561
562 =head3 _get_row_usages
563
564 Returns the total and monthly usages for a row
565
566 =cut
567
568 sub _get_row_usages {
569     my ( $self, $row, $metric_type ) = @_;
570
571     my @usage_months = $self->_get_usage_months( $self->{sushi}->{header} );
572
573     my @usage_months_fields = ();
574     my $count_total         = 0;
575
576     foreach my $usage_month (@usage_months) {
577         my $month_is_empty = 1;
578
579         foreach my $performance ( @{ $row->{Performance} } ) {
580             my $period             = $performance->{Period};
581             my $period_usage_month = substr( $period->{Begin_Date}, 0, 7 );
582
583             my $instances = $performance->{Instance};
584             my @metric_type_count =
585               map( $_->{Metric_Type} eq $metric_type ? $_->{Count} : (),
586                 @{$instances} );
587
588             if ( $period_usage_month eq $usage_month && $metric_type_count[0] )
589             {
590                 push( @usage_months_fields, $metric_type_count[0] );
591                 $count_total += $metric_type_count[0];
592                 $month_is_empty = 0;
593             }
594         }
595
596         if ($month_is_empty) {
597             push( @usage_months_fields, 0 );
598         }
599     }
600     return ( $count_total, @usage_months_fields );
601 }
602
603 =head3 _COUNTER_report_body
604
605 Return the COUNTER report body as an array
606
607 =cut
608
609 sub _COUNTER_report_body {
610     my ($self) = @_;
611
612     my $header = $self->{sushi}->{header};
613     my $body   = $self->{sushi}->{body};
614
615     my @metric_types_string = $self->_get_SUSHI_Name_Value( $header->{Report_Filters}, "Metric_Type" );
616     my @metric_types        = split( /\|/, $metric_types_string[0] );
617
618     my @report_body = ();
619
620     my $total_records = 0;
621     foreach my $report_row ( @{$body} ) {
622
623         my @metric_types = ();
624
625         # Grab all metric_types this SUSHI result has statistics for
626         foreach my $performance ( @{ $report_row->{Performance} } ) {
627             my @SUSHI_metric_types =
628               map( $_->{Metric_Type}, @{ $performance->{Instance} } );
629
630             foreach my $sushi_metric_type (@SUSHI_metric_types) {
631                 push( @metric_types, $sushi_metric_type )
632                   unless grep { $_ eq $sushi_metric_type } @metric_types;
633             }
634         }
635
636         # Add one report row for each metric_type we're working with
637         foreach my $metric_type (@metric_types) {
638             push( @report_body,
639                 $self->_COUNTER_report_row( $report_row, $metric_type ) );
640         }
641         $self->{total_records} = ++$total_records;
642     }
643
644     return @report_body;
645 }
646
647 =head3 _get_SUSHI_Name_Value
648
649 Returns "Value" of a given "Name"
650
651 =cut
652
653 sub _get_SUSHI_Name_Value {
654     my ( $self, $item, $name ) = @_;
655
656     my @value = map( $_->{Name} eq $name ? $_->{Value} : (), @{$item} );
657
658     return $value[0];
659 }
660
661 =head3 _get_SUSHI_Type_Value
662
663 Returns "Value" of a given "Type"
664
665 =cut
666
667 sub _get_SUSHI_Type_Value {
668     my ( $self, $item, $type ) = @_;
669
670     my @value = map( $_->{Type} eq $type ? $_->{Value} : (), @{$item} );
671
672     return $value[0];
673 }
674
675 =head3 _COUNTER_report_column_headings
676
677 Returns column headings by report type
678   Check the report type from the COUNTER header
679   and return column headings accordingly
680
681 =cut
682
683 sub _COUNTER_report_column_headings {
684     my ($self) = @_;
685
686     my $header = $self->{sushi}->{header};
687
688     if ( $header->{Report_ID} =~ /PR/i ) {
689         return $self->_COUNTER_platforms_report_column_headings;
690     }
691     elsif ( $header->{Report_ID} =~ /DR/i ) {
692         return $self->_COUNTER_databases_report_column_headings;
693     }
694     elsif ( $header->{Report_ID} =~ /IR/i ) {
695         return $self->_COUNTER_items_report_column_headings;
696     }
697     elsif ( $header->{Report_ID} =~ /TR/i ) {
698         return $self->_COUNTER_titles_report_column_headings;
699     }
700
701     return;
702 }
703
704 =head3 _COUNTER_items_report_column_headings
705
706 Return items report column headings
707
708 =cut
709
710 sub _COUNTER_items_report_column_headings {
711     my ($self) = @_;
712
713     my $header         = $self->{sushi}->{header};
714     my @month_headings = $self->_get_usage_months( $header, 1 );
715
716     return (
717         [
718             "Item",
719             "Publisher",
720             "Publisher_ID",
721             "Platform",
722
723             # "Authors", #IR_A1 only
724             # "Publication_Date", #IR_A1 only
725             # "Article_Version", #IR_A1 only
726             "DOI",
727             "Proprietary_ID",
728
729             # "ISBN", #IR only
730             # "Print_ISSN", #IR_A1 only
731             # "Online_ISSN", #IR_A1 only
732             "URI",
733
734             # "Parent_Title", #IR_A1 only
735             # "Parent_Authors", #IR_A1 only
736             # "Parent_Publication_Date", #IR only
737             # "Parent_Article_Version", #IR_A1 only
738             # "Parent_Data_Type", #IR only
739             # "Parent_DOI", #IR_A1 only
740             # "Parent_Proprietary_ID", #IR_A1 only
741             # "Parent_ISBN", #IR only
742             # "Parent_Print_ISSN", #IR_A1 only
743             # "Parent_Online_ISSN", #IR_A1 only
744             # "Parent_URI", #IR_A1 only
745             # "Component_Title", #IR only
746             # "Component_Authors", #IR only
747             # "Component_Publication_Date", #IR only
748             # "Component_Data_Type", #IR only
749             # "Component_DOI", #IR only
750             # "Component_Proprietary_ID", #IR only
751             # "Component_ISBN", #IR only
752             # "Component_Print_ISSN", #IR only
753             # "Component_Online_ISSN", #IR only
754             # "Component_URI", #IR only
755             # "Data_Type", #IR only
756             # "YOP", #IR only
757             # "Access_Type", #IR_A1 only
758             # "Access_Method", #IR only
759             "Metric_Type",
760             "Reporting_Period_Total",
761
762 # @month_headings in "Mmm-yyyy" format. TODO: Show unless Exclude_Monthly_Details=true
763             @month_headings
764         ]
765     );
766 }
767
768 =head3 _COUNTER_databases_report_column_headings
769
770 Return databases report column headings
771
772 =cut
773
774 sub _COUNTER_databases_report_column_headings {
775     my ($self) = @_;
776
777     my $header         = $self->{sushi}->{header};
778     my @month_headings = $self->_get_usage_months( $header, 1 );
779
780     return (
781         [
782             "Database",
783             "Publisher",
784             "Publisher_ID",
785             "Platform",
786             "Proprietary_ID",
787             "Metric_Type",
788             "Reporting_Period_Total",
789
790 # @month_headings in "Mmm-yyyy" format. TODO: Show unless Exclude_Monthly_Details=true
791             @month_headings
792         ]
793     );
794 }
795
796 =head3 _COUNTER_platforms_report_column_headings
797
798 Return platforms report column headings
799
800 =cut
801
802 sub _COUNTER_platforms_report_column_headings {
803     my ($self) = @_;
804
805     my $header         = $self->{sushi}->{header};
806     my @month_headings = $self->_get_usage_months( $header, 1 );
807
808     return (
809         [
810             "Platform",
811             "Metric_Type",
812             "Reporting_Period_Total",
813
814 # @month_headings in "Mmm-yyyy" format. TODO: Show unless Exclude_Monthly_Details=true
815             @month_headings
816         ]
817     );
818 }
819
820 =head3 _COUNTER_titles_report_column_headings
821
822 Return titles report column headings
823
824 =cut
825
826 sub _COUNTER_titles_report_column_headings {
827     my ($self) = @_;
828
829     my $header         = $self->{sushi}->{header};
830     my @month_headings = $self->_get_usage_months( $header, 1 );
831     my $specific_fields =
832       $self->get_report_type_specific_fields( $header->{Report_ID} );
833
834     return (
835         [
836             "Title",
837             "Publisher",
838             "Publisher_ID",
839             "Platform",
840             "DOI",
841             "Proprietary_ID",
842             grep ( /ISBN/, @{$specific_fields} ) ? ("ISBN") : (),
843             "Print_ISSN",
844             "Online_ISSN",
845             "URI",
846
847             #"Data_Type", #TODO: Only if requested (?)
848             #"Section_Type", #TODO: Only if requested (?)
849             grep ( /YOP/,         @{$specific_fields} ) ? ("YOP")         : (),
850             grep ( /Access_Type/, @{$specific_fields} ) ? ("Access_Type") : (),
851
852             #"Access_Method", #TODO: Only if requested (?)
853             "Metric_Type",
854             "Reporting_Period_Total",
855
856 # @month_headings in "Mmm-yyyy" format. TODO: Show unless Exclude_Monthly_Details=true
857             @month_headings
858         ]
859     );
860 }
861
862 =head3 _get_usage_months
863
864 Return report usage months. Formatted for column headings if $column_headings_formatting
865
866 =cut
867
868 sub _get_usage_months {
869     my ( $self, $header, $column_headings_formatting ) = @_;
870
871     my @months = (
872         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
873         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
874     );
875
876     my @begin_date = map( $_->{Name} eq "Begin_Date" ? $_->{Value} : (),
877         @{ $header->{Report_Filters} } );
878     my $begin_month = substr( $begin_date[0], 5, 2 );
879     my $begin_year  = substr( $begin_date[0], 0, 4 );
880
881     my @end_date = map( $_->{Name} eq "End_Date" ? $_->{Value} : (),
882         @{ $header->{Report_Filters} } );
883     my $end_month = substr( $end_date[0], 5, 2 );
884     my $end_year  = substr( $end_date[0], 0, 4 );
885
886     my @month_headings = ();
887     while ( $begin_month <= $end_month || $begin_year < $end_year ) {
888         push( @month_headings,
889               $column_headings_formatting
890             ? $months[ $begin_month - 1 ] . " " . $begin_year
891             : $begin_year . "-" . $begin_month );
892         $begin_month++;
893         if ( $begin_month > 12 ) {
894             $begin_month = 1;
895             $begin_year++;
896         }
897         $begin_month = "0" . $begin_month if length($begin_month) == 1;
898     }
899
900     return @month_headings;
901 }
902
903 =head3 get_report_type_specific_fields
904
905 Returns the specific fields for a given report_type
906
907 =cut
908
909 sub get_report_type_specific_fields {
910     my ( $self, $report_type ) = @_;
911
912     my %report_type_map = (
913         "TR_B1" => [ 'YOP', 'ISBN' ],
914         "TR_B2" => [ 'YOP', 'ISBN' ],
915         "TR_B3" => [ 'YOP', 'Access_Type', 'ISBN' ],
916         "TR_J3" => ['Access_Type'],
917         "TR_J4" => ['YOP'],
918     );
919
920     return $report_type_map{$report_type};
921
922 }
923
924 =head3 test_connection
925
926 Tests the connection of the harvester to the SUSHI service and returns any alerts of planned SUSHI outages
927
928 =cut
929
930 sub test_connection {
931     my ($self) = @_;
932
933     my $url = $self->service_url;
934     $url .= '/status';
935     $url .= '?customer_id=' . $self->customer_id;
936     $url .= '&requestor_id=' . $self->requestor_id if $self->requestor_id;
937     $url .= '&api_key=' . $self->api_key           if $self->api_key;
938
939     my $request  = HTTP::Request->new( 'GET' => $url );
940     my $ua       = LWP::UserAgent->new;
941     my $response = $ua->simple_request($request);
942
943     my @result = decode_json( $response->decoded_content );
944     if ( $result[0][0]->{Service_Active} ) {
945         return 1;
946     }
947     else {
948         return 0;
949     }
950
951 }
952
953 =head3 erm_usage_titles
954
955 Method to embed erm_usage_titles to titles for report formatting
956
957 =cut
958
959 sub erm_usage_titles {
960     my ($self) = @_;
961     my $usage_title_rs = $self->_result->erm_usage_titles;
962     return Koha::ERM::UsageTitles->_new_from_dbic($usage_title_rs);
963 }
964
965 =head3 erm_usage_muses
966
967 Method to embed erm_usage_muses to titles for report formatting
968
969 =cut
970
971 sub erm_usage_muses {
972     my ($self) = @_;
973     my $usage_mus_rs = $self->_result->erm_usage_muses;
974     return Koha::ERM::MonthlyUsages->_new_from_dbic($usage_mus_rs);
975 }
976
977
978
979 =head3 _type
980
981 =cut
982
983 sub _type {
984     return 'ErmUsageDataProvider';
985 }
986
987 1;