File Coverage

blib/lib/WWW/ArsenalFC/TicketInformation.pm
Criterion Covered Total %
statement 114 114 100.0
branch 15 16 93.7
condition 2 2 100.0
subroutine 16 16 100.0
pod 1 1 100.0
total 148 149 99.3


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__