line
stmt
bran
cond
sub
pod
time
code
1
package WWW::eiNetwork;
2
3
1
1
592
use strict;
1
2
1
29
4
1
1
5
use warnings;
1
2
1
22
5
1
1
5
use Carp;
1
5
1
89
6
1
1
1696
use HTML::TableContentParser;
0
0
7
use WWW::Mechanize;
8
9
our $VERSION = '0.02';
10
11
sub new
12
{
13
my ($class, %args) = @_;
14
15
croak "You must specify your library card number" unless $args{card_number};
16
croak "You must specify your PIN number" unless $args{pin_number};
17
18
# Strip trailing slash from URL prefix
19
my $prefix = $args{url_prefix} || '';
20
$prefix =~ s/\/$//;
21
22
my $self =
23
{
24
card_number => $args{card_number},
25
pin_number => $args{pin_number},
26
url_prefix => $prefix || 'https://iiisy1.einetwork.net/patroninfo~S1',
27
};
28
29
bless $self, $class;
30
return $self;
31
};
32
33
sub _login
34
{
35
my ($self, $name, $card) = @_;
36
37
my $mech = WWW::Mechanize->new;
38
$mech->get($self->{url_prefix});
39
$mech->form_with_fields(qw(code pin));
40
$mech->field('code', $self->{card_number});
41
$mech->field('pin', $self->{pin_number});
42
$mech->click('submit');
43
44
my $uri = $mech->uri;
45
if ($uri =~ /patroninfo~S1\/(\d+)\//)
46
{
47
$self->{patron_id} = $1;
48
$self->{mech} = $mech;
49
return $self->{mech};
50
}
51
else
52
{
53
croak "Couldn't log in to eiNetwork!";
54
return;
55
}
56
}
57
58
sub holds
59
{
60
my ($self, %args) = @_;
61
62
my @classes = ('Title', 'Status', 'Pickup', 'Cancel');
63
my @items = $self->_get_content(
64
page => 'holds',
65
classes => \@classes,
66
html => $args{html},
67
);
68
69
return wantarray ? @items : \@items;
70
}
71
72
sub items
73
{
74
my ($self, %args) = @_;
75
76
my @classes = ('Title', 'Barcode', 'Status', 'CallNo');
77
my @items = $self->_get_content(
78
page => 'items',
79
classes => \@classes,
80
html => $args{html},
81
);
82
83
return wantarray ? @items : \@items;
84
}
85
86
sub _get_content
87
{
88
my ($self, %args) = @_;
89
90
my $page = $args{page};
91
my $classes = $args{classes};
92
my $html = $args{html};
93
94
# Hack to facilitate unit tests
95
$html ||= $self->_get_html($page);
96
97
my $tables = $self->_get_tables($html);
98
99
my @items;
100
for my $table (@$tables)
101
{
102
next unless ($table->{class} and $table->{class} eq 'patFunc');
103
for my $row (@{$table->{rows}})
104
{
105
next unless ($row->{class} and $row->{class} eq 'patFuncEntry');
106
my %record;
107
for my $cell (@{$row->{cells}})
108
{
109
for my $class (@$classes)
110
{
111
if ($cell->{class} and $cell->{class} eq "patFunc$class")
112
{
113
my $data = $self->_cleanup_data($cell->{data});
114
$record{lc($class)} = $data;
115
next;
116
}
117
}
118
}
119
120
push @items, \%record;
121
}
122
}
123
124
return wantarray ? @items : \@items;
125
}
126
127
sub _get_html
128
{
129
my ($self, $page) = @_;
130
131
$self->_login or croak "Couldn't log in!";
132
133
my $mech = $self->{mech};
134
return unless $mech;
135
136
my $patron_id = $self->{patron_id};
137
return unless $patron_id;
138
139
my $prefix = $self->{url_prefix};
140
return unless $prefix;
141
142
$mech->get("$prefix/$patron_id/$page");
143
return $mech->content;
144
}
145
146
sub _get_tables
147
{
148
my ($self, $html) = @_;
149
150
my $tp = HTML::TableContentParser->new();
151
my $tables = $tp->parse($html);
152
return $tables;
153
}
154
155
sub _cleanup_data
156
{
157
my ($self, $data) = @_;
158
159
# If the result is a link, strip the link tags and use the title.
160
# Not the greatest regex, but works for these simple cases.
161
if ($data =~ /"\>\s*(.*)\s*<\/a>/m)
162
{
163
$data = $1;
164
}
165
166
# If the data is a select and there's something selected, use the
167
# title of the selected option.
168
if ($data =~ /\
169
{
170
$data =~ /selected="selected">\s*(.*)\s*<\/option>/m;
171
$data = $1;
172
}
173
174
# Remove leading and trailing whitespace.
175
$data =~ s/^\s*//;
176
$data =~ s/\s*$//;
177
178
return $data;
179
}
180
181
1;
182
183
184
=head1 NAME
185
186
WWW::eiNetwork - Perl interface to Allegheny County, PA libraries
187
188
=head1 SYNOPSIS
189
190
use WWW::eiNetwork;
191
192
my $ein = WWW::eiNetwork->new(
193
card_number => '23456000000000',
194
pin_number => '1234',
195
url_prefix => 'https://iiisy1.einetwork.net/patroninfo~S1', #optional
196
);
197
198
my @holds = $ein->holds;
199
my @items = $ein->items;
200
201
for my $hold (@holds)
202
{
203
print qq(
204
Title: $hold->{title}
205
Status: $hold->{status}
206
Pickup at: $hold->{pickup}
207
Cancel if not picked up by: $hold->{cancel}\n\n
208
);
209
}
210
211
for my $item (@items)
212
{
213
print qq(
214
Title: $item->{title}
215
Barcode: $item->{barcode}
216
Status: $item->{status}
217
CallNo: $item->{callno}\n\n
218
);
219
}
220
221
=head1 DESCRIPTION
222
223
This module provides an object-oriented Perl interface to eiNetwork libraries in Allegheny County, Pennsylvania.
224
225
=head1 DEPENDENCIES
226
227
WWW::Mechanize, HTML::TableContentParser, Crypt::SSLeay or IO::Socket::SSL
228
229
=head1 BUGS
230
231
The eiNetwork doesn't provide a public API - this module uses screen scraping to pull data directly from the HTML on their site. While I made an effort to code this module in such a way that small changes to the site layout and table arrangement won't break the module, any number of changes to the EIN's site could break this module.
232
233
=head1 DISCLAIMER
234
235
The author of this module is not affiliated in any way with the EINetwork or any Allegheny County library.
236
237
=head1 ACKNOWLEDGMENTS
238
239
Thanks to:
240
241
Adam Foxson (L) for the great newbie's tutorial to contributing to CPAN at the Pittsburgh Perl Workshop (L ).
242
243
Bob O'Neill (L) for sharing his CPAN know-how.
244
245
=head1 COPYRIGHT AND LICENSE
246
247
Copyright (C) 2008 Michael Aquilina. All rights reserved.
248
249
This code is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
250
251
=head1 AUTHOR
252
253
Michael Aquilina, aquilina@cpan.org
254
255
=cut