View | Details | Raw Unified | Return to bug 20570
Collapse All | Expand All

(-)a/Koha/ArticleRequests.pm (+125 lines)
Lines 20-27 package Koha::ArticleRequests; Link Here
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Carp;
22
use Carp;
23
use Data::Dumper qw/Dumper/;
23
24
24
use Koha::Database;
25
use Koha::Database;
26
use Koha::DateUtils qw/dt_from_string/;
25
27
26
use Koha::ArticleRequest;
28
use Koha::ArticleRequest;
27
use Koha::ArticleRequest::Status;
29
use Koha::ArticleRequest::Status;
Lines 82-87 sub canceled { Link Here
82
    return Koha::ArticleRequests->search( $params );
84
    return Koha::ArticleRequests->search( $params );
83
}
85
}
84
86
87
=head3 match_scans
88
89
    $class->match_scans({ uploadcategory => 'SCANS' });
90
91
    Match all requests in processing stage with uploaded files in the
92
    specified category submitted later than the first request was set
93
    to processing. Save upload URL in the requests and mark complete.
94
95
    Groups of uploaded files submitted within the same interval as described
96
    by the optional interval parameter (default 10) are considered as
97
    batches to be sorted on filename. Disable this behavior by passing -1.
98
    In that case the strict upload order is followed.
99
100
=cut
101
102
sub match_scans {
103
    my ( $class, $params ) = @_;
104
    my $category = $params->{uploadcategory};
105
    my $interval = $params->{interval} // 10; # minutes
106
107
    my $requests = $class->search_scans_in_process->as_list;
108
    return if !@$requests; # no requests to match
109
110
    # Find time to start looking for uploads
111
    my $first_processed = $requests->[0]->updated_on;
112
113
    #FIXME Replace with Koha::UploadedFiles
114
    # Public should be true, since we will create OPAC URLs
115
    my $uploads = C4::Context->dbh->selectall_arrayref("SELECT filename,hashvalue,dtcreated FROM uploaded_files WHERE uploadcategorycode=? AND dtcreated>? AND public=1 ORDER BY id", undef, ( $category, $first_processed ));
116
    return if !@$uploads;
117
118
    # Split uploads in batches (based on interval). We can assume that the
119
    # uploads have ascending timestamps.
120
    my @batches;
121
    my $n = 0;
122
    my $dt_last;
123
    foreach my $u (@$uploads) {
124
        my $dt = dt_from_string( $u->[2], 'sql' );
125
        if( !defined $dt_last ) {
126
            $dt_last = $dt;
127
        } elsif( $dt_last->delta_ms( $dt )->in_units('minutes') > $interval ) {
128
            # delta_ms returns non-negative values only
129
            $dt_last = $dt;
130
            push @batches, $n;
131
            $n = 0;
132
        }
133
        $n++;
134
    }
135
    push @batches, $n if $n;
136
137
    # Rearrange order of uploads within a batch on filename
138
    # This is done since the order of multiple selected files in the file
139
    # upload control is not reliable (often in reverse).
140
    my $pos = 0;
141
    foreach my $s (@batches) {
142
        @$uploads[$pos..$pos+$s-1] = sort { lc($a->[0]) cmp lc($b->[0]) } @$uploads[$pos..$pos+$s-1] if $s > 1;
143
        $pos += $s;
144
    }
145
146
    # Match while we have uploads or requests
147
    my $result = {};
148
    while( @$uploads && @$requests ) {
149
        my $upload = shift @$uploads;
150
        my $req = shift @$requests;
151
        my $url = C4::Context->preference('OPACBaseURL').'/cgi-bin/koha/opac-retrieve-file.pl?id='.$upload->[1];
152
        $req->urls($url)->store;
153
        $req->complete;
154
    }
155
}
156
157
=head3 search_scans_in_process
158
159
    $class->search_scans_in_process({ list => 1 });
160
161
    Search for article requests with status in processing, format SCAN
162
    and empty urls field. Order by last change.
163
164
    The optional list parameter (used in opac/svc/scan_requests) makes
165
    that an arrayref of request id's is returned.
166
    Without it, a Koha::Objects search is returned (used by match_scans).
167
168
=cut
169
170
sub search_scans_in_process {
171
    my ( $class, $params  ) = @_;
172
    my $req_in_process = $class->search(
173
        { status => 'PROCESSING', format => 'SCAN', urls => [ '', undef ] },
174
        { order_by => 'updated_on' },
175
    );
176
    if( $params->{list} ) {
177
        return [ map { $_->id } $req_in_process->as_list ];
178
    }
179
    return $req_in_process;
180
}
181
182
=head3 complete_scans
183
184
    Save URLs in scan requests and mark them as completed.
185
186
    The $scans parameter is a hashref of the form { request_id => url, ... }
187
188
    Returns the number of completed scan requests.
189
190
    Note: The routine is called by opac/svc/scan_requests?op=complete when
191
    you pass the results of external matching (as opposed to match_scans)
192
    back to Koha. This allows for customization with external web services.
193
194
=cut
195
196
sub complete_scans {
197
    my ( $class, $scans ) = @_;
198
    my $n = 0;
199
    foreach my $id ( keys %$scans ) {
200
        my $req = Koha::ArticleRequests->find( $id );
201
        if( $req ) {
202
            $req->urls( $scans->{$id} )->store;
203
            $req->complete;
204
            $n++;
205
        }
206
    }
207
    return $n;
208
}
209
85
=head3 _type
210
=head3 _type
86
211
87
=cut
212
=cut
(-)a/misc/migration_tools/complete_article_requests.pl (+121 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
use Getopt::Long;
5
use Pod::Usage;
6
use C4::Context;
7
use Koha::ArticleRequests;
8
9
my $params = {};
10
GetOptions(
11
    'uploadcategory:s'  => \$params->{uploadcategory},
12
    'upload_interval:i' => \$params->{upload_interval},
13
    'commit'            => \$params->{commit},
14
    'help'              => \$params->{help},
15
    'manual'            => \$params->{manual},
16
);
17
18
if( $params->{help} ) {
19
    pod2usage();
20
} elsif( $params->{manual} ) {
21
    pod2usage({ -verbose => 2 });
22
} elsif( !C4::Context->preference('ArticleRequests') ||
23
  C4::Context->preference('ArticleRequestsSupportedFormats') !~ /SCAN/ ) {
24
    print "First enable ArticleRequests and add SCAN as supported format.\n";
25
} elsif( !$params->{uploadcategory} ) {
26
    print "Please specify an uploadcategory.\n";
27
    pod2usage();
28
} elsif( !$params->{commit} ) {
29
    print "You need to specify the commit parameter.\n";
30
    pod2usage();
31
} else {
32
    Koha::ArticleRequests->match_scans({ uploadcategory => $params->{uploadcategory}, interval => $params->{upload_interval} });
33
}
34
35
=head1 NAME
36
37
complete_article_requests.pl
38
39
=head1 COPYRIGHT
40
41
Copyright 2018 Rijksmuseum
42
43
=head1 LICENSE
44
45
This file is part of Koha.
46
47
Koha is free software; you can redistribute it and/or modify it under the
48
terms of the GNU General Public License as published by the Free Software
49
Foundation; either version 3 of the License, or (at your option) any later
50
version.
51
52
Koha is distributed in the hope that it will be useful, but WITHOUT ANY
53
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
54
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
55
56
You should have received a copy of the GNU General Public License along
57
with Koha; if not, write to the Free Software Foundation, Inc.,
58
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
59
60
=head1 SYNOPSIS
61
62
  complete_article_requests.pl -help
63
  complete_article_requests.pl -manual
64
  complete_article_requests.pl -uploadcategory SCANS -commit
65
66
=head1 DESCRIPTION
67
68
This script serves to complete scanned article requests by matching them with
69
uploads in a specified upload category.
70
71
How does it work? The script is based on the following criteria:
72
73
  [1] The order of article requests in processing stage is followed
74
      according to the updated_on timestamp.
75
76
  [2] Only uploaded files in the specified category, created later than the
77
      timestamp of the first processing request are considered.
78
79
  [3] The uploads should have been marked as Public.
80
81
  [4] The algorithm tries to correct multiple files submitted in reverse order
82
      by sorting on filename per group of files uploaded within a specfied
83
      number of minutes (upload_interval). If you set this number to -1,
84
      the exact upload order will be used.
85
 
86
In the process of linking files to requests, the urls field is filled and the
87
request is marked completed (triggering a mail to the requestor).
88
89
=head1 OPTIONS
90
91
=head2 help
92
93
Print usage statement
94
95
=head2 manual
96
97
Print complete POD documentation
98
99
=head2 uploadcategory
100
101
Code of the Koha upload category that you are using for scanned article
102
request materials.
103
104
=head2 upload_interval
105
106
Optional parameter to specify within how many minutes a series of consecutive
107
uploads are considered as a batch or group to be sorted on filename. The
108
match_scans method has a fallback default value.
109
110
=head1 ADDITIONAL COMMENTS
111
112
Instead of using the Upload feature in connection with this script, Koha also
113
allows you to automate matching files with requests externally by offering
114
the script opac/svc/scan_requests with op=list and op=complete. That script
115
contains more documentation about its use.
116
117
=head1 AUTHOR
118
119
Marcel de Rooy, Rijksmuseum Amsterdam, The Netherlands
120
121
=cut
(-)a/opac/svc/scan_requests (-1 / +92 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
use CGI qw ( -utf8 );
5
use C4::Context;
6
use Koha::ArticleRequests;
7
use JSON;
8
9
use constant CONFIG_SVC_TOKEN => 'svc_token_scans';
10
11
my $query = CGI->new;
12
my $op = $query->param('op');
13
my $token = $query->param('token');
14
my $uploads = $query->param('uploads');
15
16
my $json = JSON->new;
17
$json->pretty(0);
18
$json->utf8;
19
20
my $data = {};
21
if( !$token || $token ne C4::Context->config(CONFIG_SVC_TOKEN) ) {
22
    $data = { error => 1 };
23
} elsif( $op eq 'list' ) {
24
    $data = Koha::ArticleRequests->search_scans_in_process({ list => 1 });
25
} elsif( $op eq 'complete' ) {
26
    # uploads should look like: { id1 => 'URL;URL', ... }
27
    $uploads = $json->decode($uploads);
28
    my $cnt = Koha::ArticleRequests->complete_scans($uploads);
29
    $data = { count => $cnt };
30
} else {
31
    $data = { error => 2 };
32
}
33
print $query->header( -type => 'application/json', -charset => 'UTF-8' );
34
print $json->encode($data);
35
36
=head1 NAME
37
38
opac/svc/scan_requests
39
40
=head1 COPYRIGHT
41
42
Copyright 2018 Rijksmuseum
43
44
=head1 LICENSE
45
46
This file is part of Koha.
47
48
Koha is free software; you can redistribute it and/or modify it under the
49
terms of the GNU General Public License as published by the Free Software
50
Foundation; either version 3 of the License, or (at your option) any later
51
version.
52
53
Koha is distributed in the hope that it will be useful, but WITHOUT ANY
54
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
55
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
56
57
You should have received a copy of the GNU General Public License along
58
with Koha; if not, write to the Free Software Foundation, Inc.,
59
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
60
61
=head1 SYNOPSIS
62
63
  GET https://[your Koha server]/cgi-bin/koha/scan_requests?op=list
64
  POST https://[your Koha server]/cgi-bin/koha/scan_requests
65
    (Include op=complete, token=... and uploads=...
66
67
=head1 DESCRIPTION
68
69
This service script (candidate for REST API) allows you to obtain a list of
70
processing scan requests and allows you to save URLs for processed scans and
71
complete the requests.
72
73
By using the script you can externalize the matching of open requests and
74
scanned files, including uploading files to an external web service. Your
75
solution should just call scan_request at the start with op=list and call
76
again at the end with op=complete, presenting a token for security and
77
formatting the results in the described format.
78
79
The uploads parameter should be a JSON representation of a hash structure
80
like below, where you see request id's and URLs.
81
82
  { 1234 => 'https://domain.com/file1', 1235 => 'https://domain.com/file2' }
83
84
Temporarily, the script uses a token to be saved in koha-conf.xml under the
85
key svc_token_scans. This must be moved later to REST API with appropriate
86
authorization.
87
88
=head1 AUTHOR
89
90
Marcel de Rooy, Rijksmuseum Amsterdam, The Netherlands
91
92
=cut

Return to bug 20570