line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
2
|
|
|
2
|
|
403210
|
use v5.10.1; |
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
283
|
|
2
|
2
|
|
|
2
|
|
15
|
use strict; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
92
|
|
3
|
2
|
|
|
2
|
|
16
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
476
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package WWW::ArsenalFC::TicketInformation; |
6
|
|
|
|
|
|
|
{ |
7
|
|
|
|
|
|
|
$WWW::ArsenalFC::TicketInformation::VERSION = '1.123160'; |
8
|
|
|
|
|
|
|
} |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# ABSTRACT: Get Arsenal FC ticket information for forthcoming matches |
11
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
2512
|
use WWW::ArsenalFC::TicketInformation::Match; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
24
|
|
13
|
2
|
|
|
2
|
|
317
|
use WWW::ArsenalFC::TicketInformation::Match::Availability; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
16
|
|
14
|
2
|
|
|
2
|
|
2763
|
use WWW::ArsenalFC::TicketInformation::Category; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
26
|
|
15
|
|
|
|
|
|
|
|
16
|
2
|
|
|
2
|
|
3873
|
use LWP::Simple (); |
|
2
|
|
|
|
|
411756
|
|
|
2
|
|
|
|
|
68
|
|
17
|
2
|
|
|
2
|
|
2624
|
use HTML::TreeBuilder::XPath (); |
|
2
|
|
|
|
|
305079
|
|
|
2
|
|
|
|
|
64
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# the URL on Arsenal.com |
20
|
2
|
|
|
2
|
|
30
|
use constant URL => 'http://www.arsenal.com/membership/buy-tickets'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
19969
|
|
21
|
|
|
|
|
|
|
|
22
|
2
|
|
|
|
|
67
|
use Object::Tiny qw{ |
23
|
|
|
|
|
|
|
categories |
24
|
|
|
|
|
|
|
matches |
25
|
2
|
|
|
2
|
|
26
|
}; |
|
2
|
|
|
|
|
4
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub fetch { |
28
|
2
|
|
|
2
|
1
|
727007
|
my ($self) = @_; |
29
|
|
|
|
|
|
|
|
30
|
2
|
|
|
|
|
14
|
$self->_fetch_categories(); |
31
|
2
|
|
|
|
|
12
|
$self->_fetch_matches(); |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub _fetch_categories { |
35
|
2
|
|
|
2
|
|
6
|
my ($self) = @_; |
36
|
|
|
|
|
|
|
|
37
|
2
|
|
|
|
|
12
|
my $tree = $self->_get_tree(); |
38
|
2
|
|
|
|
|
5
|
my @categories; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# get the categories table |
41
|
2
|
|
|
|
|
44
|
my $rows = $tree->findnodes('//table[@summary="Match categories"]//tr'); |
42
|
|
|
|
|
|
|
|
43
|
2
|
|
|
|
|
589852
|
my %categories_hash = (); |
44
|
2
|
|
|
|
|
15
|
for ( my $i = 1 ; $i < $rows->size() ; $i++ ) { |
45
|
10
|
|
|
|
|
129
|
my $row = $rows->[$i]; |
46
|
|
|
|
|
|
|
|
47
|
10
|
|
|
|
|
38
|
my $category = WWW::ArsenalFC::TicketInformation::Category->new( |
48
|
|
|
|
|
|
|
date_string => $row->findvalue('td[1]'), |
49
|
|
|
|
|
|
|
opposition => $row->findvalue('td[2]'), |
50
|
|
|
|
|
|
|
category => $row->findvalue('td[3]'), |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
|
53
|
10
|
|
|
|
|
20974
|
push @categories, $category; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# used to assign the category to a match later |
56
|
10
|
|
|
|
|
343
|
my $category_key = |
57
|
|
|
|
|
|
|
sprintf( "%s:%s", $category->opposition, $category->date ); |
58
|
10
|
|
|
|
|
246
|
$categories_hash{$category_key} = $category->category; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
2
|
|
|
|
|
25
|
$self->{categories} = \@categories; |
62
|
2
|
|
|
|
|
12
|
$self->{categories_hash} = \%categories_hash; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub _fetch_matches { |
66
|
2
|
|
|
2
|
|
5
|
my ($self) = @_; |
67
|
|
|
|
|
|
|
|
68
|
2
|
|
|
|
|
9
|
my $tree = $self->_get_tree(); |
69
|
2
|
|
|
|
|
5
|
my @matches; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# get the table and loop over every 3 rows, as these |
72
|
|
|
|
|
|
|
# contain the matches |
73
|
|
|
|
|
|
|
# the second and third rows contain data on who can purchase tickets, if |
74
|
|
|
|
|
|
|
# its not yet sold out or on the exchange. |
75
|
2
|
|
|
|
|
12
|
my $rows = $tree->findnodes('//table[@id="member-tickets"]/tr'); |
76
|
2
|
|
|
|
|
489182
|
for ( my $i = 0 ; $i < $rows->size() ; $i += 3 ) { |
77
|
9
|
|
|
|
|
67
|
my %match = (); |
78
|
9
|
|
|
|
|
17
|
my $row = $rows->[$i]; |
79
|
|
|
|
|
|
|
|
80
|
9
|
|
|
|
|
41
|
$match{fixture} = _trimWhitespace( $row->findvalue('td[2]/p[1]') ); |
81
|
9
|
|
|
|
|
44
|
$match{competition} = _trimWhitespace( $row->findvalue('td[2]/p[2]') ); |
82
|
9
|
|
|
|
|
33
|
$match{datetime_string} = |
83
|
|
|
|
|
|
|
_trimWhitespace( $row->findvalue('td[2]/p[3]') ); |
84
|
9
|
|
|
|
|
44
|
$match{hospitality} = $row->exists( |
85
|
|
|
|
|
|
|
'td[3]//a[@href="http://www.arsenal.com/hospitality/events"]'); |
86
|
|
|
|
|
|
|
|
87
|
9
|
|
|
|
|
17645
|
$match{is_soldout} = $row->exists('td[6]//span[@class="soldout"]'); |
88
|
9
|
|
|
|
|
12946
|
$match{can_exchange} = 0; |
89
|
|
|
|
|
|
|
|
90
|
9
|
100
|
|
|
|
35
|
if ( !$match{is_soldout} ) { |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
AVAILABILITY: |
93
|
7
|
|
|
|
|
34
|
for ( my $j = $i ; $j < $i + 3 ; $j++ ) { |
94
|
16
|
|
|
|
|
145
|
my $availability_row = $rows->[$j]; |
95
|
|
|
|
|
|
|
|
96
|
16
|
|
|
|
|
17
|
my @membership_nodes; |
97
|
16
|
100
|
|
|
|
45
|
if ( $j == $i ) { |
98
|
7
|
|
|
|
|
31
|
@membership_nodes = |
99
|
|
|
|
|
|
|
$availability_row->findnodes('td[5]/img[@title]'); |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
else { |
102
|
9
|
|
|
|
|
67
|
@membership_nodes = |
103
|
|
|
|
|
|
|
$availability_row->findnodes('td[1]/img[@title]'); |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
16
|
100
|
|
|
|
28372
|
last AVAILABILITY unless @membership_nodes; |
107
|
|
|
|
|
|
|
|
108
|
10
|
|
|
|
|
17
|
my ( $availability_forsale, $availability_date ); |
109
|
10
|
100
|
|
|
|
30
|
if ( $j == $i ) { |
110
|
7
|
|
|
|
|
30
|
( $availability_forsale, $availability_date ) = |
111
|
|
|
|
|
|
|
_parse_availability( |
112
|
|
|
|
|
|
|
$availability_row->findvalue('td[6]/p') ); |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
else { |
115
|
3
|
|
|
|
|
12
|
( $availability_forsale, $availability_date ) = |
116
|
|
|
|
|
|
|
_parse_availability( |
117
|
|
|
|
|
|
|
$availability_row->findvalue('td[2]/p') ); |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
10
|
|
|
|
|
31
|
my @memberships_for_availability; |
121
|
10
|
|
|
|
|
28
|
for my $membership_node (@membership_nodes) { |
122
|
11
|
|
|
|
|
46
|
my $membership = $membership_node->attr('title'); |
123
|
11
|
|
|
|
|
144
|
given ($membership) { |
124
|
11
|
|
|
|
|
33
|
when (/Exchange/) { |
125
|
1
|
|
|
|
|
4
|
$match{can_exchange} = 1; |
126
|
1
|
|
|
|
|
5
|
last AVAILABILITY; |
127
|
|
|
|
|
|
|
} |
128
|
10
|
|
|
|
|
45
|
when (/General Sale/) { |
129
|
2
|
|
|
|
|
41
|
push( @memberships_for_availability, |
130
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
131
|
|
|
|
|
|
|
->GENERAL_SALE ); |
132
|
|
|
|
|
|
|
} |
133
|
8
|
|
|
|
|
29
|
when (/Red Members/) { |
134
|
3
|
|
|
|
|
21
|
push( @memberships_for_availability, |
135
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
136
|
|
|
|
|
|
|
->RED ); |
137
|
|
|
|
|
|
|
} |
138
|
5
|
|
|
|
|
25
|
when (/Silver Members/) { |
139
|
3
|
|
|
|
|
21
|
push( @memberships_for_availability, |
140
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
141
|
|
|
|
|
|
|
->SILVER ); |
142
|
|
|
|
|
|
|
} |
143
|
2
|
|
|
|
|
11
|
when (/Platinum \/ Gold Members/) { |
144
|
1
|
|
|
|
|
9
|
push( @memberships_for_availability, |
145
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
146
|
|
|
|
|
|
|
->PLATINUM_GOLD ); |
147
|
|
|
|
|
|
|
} |
148
|
1
|
|
|
|
|
6
|
when (/Travel Club/) { |
149
|
1
|
|
|
|
|
6
|
push( @memberships_for_availability, |
150
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
151
|
|
|
|
|
|
|
->TRAVEL_CLUB ); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
} # given ($membership) |
154
|
10
|
|
100
|
|
|
73
|
$match{availability} //= []; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
} |
157
|
9
|
100
|
|
|
|
36
|
if ($availability_forsale) { |
|
|
50
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
3
|
|
|
|
|
5
|
push @{ $match{availability} }, |
|
3
|
|
|
|
|
38
|
|
160
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
161
|
|
|
|
|
|
|
->new( |
162
|
|
|
|
|
|
|
memberships => \@memberships_for_availability, |
163
|
|
|
|
|
|
|
type => |
164
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
165
|
|
|
|
|
|
|
->FOR_SALE, |
166
|
|
|
|
|
|
|
); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
elsif ($availability_date) { |
169
|
6
|
|
|
|
|
11
|
push @{ $match{availability} }, |
|
6
|
|
|
|
|
47
|
|
170
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
171
|
|
|
|
|
|
|
->new( |
172
|
|
|
|
|
|
|
memberships => \@memberships_for_availability, |
173
|
|
|
|
|
|
|
type => |
174
|
|
|
|
|
|
|
WWW::ArsenalFC::TicketInformation::Match::Availability |
175
|
|
|
|
|
|
|
->SCHEDULED, |
176
|
|
|
|
|
|
|
date => $availability_date |
177
|
|
|
|
|
|
|
); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
} # for my $membership_node (@membership_nodes) |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
9
|
|
|
|
|
80
|
my $match = WWW::ArsenalFC::TicketInformation::Match->new(%match); |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# add the category if we can |
185
|
9
|
|
|
|
|
146
|
my $category_key = sprintf( "%s:%s", $match->opposition, $match->date ); |
186
|
9
|
100
|
|
|
|
70
|
if ( my $category = $self->{categories_hash}->{$category_key} ) { |
187
|
3
|
|
|
|
|
12
|
$match->{category} = $category; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
9
|
|
|
|
|
58
|
push @matches, $match; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
2
|
|
|
|
|
41
|
$self->{matches} = \@matches; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# populates an HTML::TreeBuilder::XPath tree, unless we already have one |
197
|
|
|
|
|
|
|
sub _get_tree { |
198
|
4
|
|
|
4
|
|
11
|
my ($self) = @_; |
199
|
|
|
|
|
|
|
|
200
|
4
|
100
|
|
|
|
34
|
if ( !$self->{tree} ) { |
201
|
1
|
|
|
|
|
11
|
$self->{tree} = |
202
|
|
|
|
|
|
|
HTML::TreeBuilder::XPath->new_from_content( LWP::Simple::get(URL) ); |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
4
|
|
|
|
|
969805
|
return $self->{tree}; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub _parse_availability { |
209
|
10
|
|
|
10
|
|
9000
|
my ($availability) = @_; |
210
|
|
|
|
|
|
|
|
211
|
10
|
|
|
|
|
23
|
given ($availability) { |
212
|
10
|
|
|
|
|
36
|
when (/Buy Now/) { |
213
|
3
|
|
|
|
|
10
|
return 1; |
214
|
|
|
|
|
|
|
} |
215
|
7
|
|
|
|
|
36
|
when (/(\d\d-\d\d-\d\d\d\d)/) { |
216
|
6
|
|
|
|
|
28
|
return ( undef, $1 ); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# trims whitespace from a string |
222
|
|
|
|
|
|
|
sub _trimWhitespace { |
223
|
27
|
|
|
27
|
|
25863
|
my $string = shift; |
224
|
27
|
|
|
|
|
100
|
$string =~ s/^\s+//; |
225
|
27
|
|
|
|
|
128
|
$string =~ s/\s+$//; |
226
|
27
|
|
|
|
|
112
|
return $string; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
1; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
__END__ |