File Coverage

blib/lib/WWW/ColiPoste.pm
Criterion Covered Total %
statement 65 75 86.6
branch 5 16 31.2
condition 2 7 28.5
subroutine 10 10 100.0
pod 2 2 100.0
total 84 110 76.3


line stmt bran cond sub pod time code
1             package WWW::ColiPoste;
2 3     3   3062 use strict;
  3         7  
  3         124  
3 3     3   18 use warnings;
  3         7  
  3         94  
4 3     3   28 use Carp;
  3         6  
  3         237  
5 3     3   3092 use File::Slurp;
  3         44293  
  3         274  
6 3     3   3283 use HTML::Entities;
  3         22002  
  3         326  
7 3     3   4061 use HTML::TreeBuilder;
  3         98859  
  3         44  
8 3     3   3910 use LWP::UserAgent;
  3         218614  
  3         134  
9              
10              
11             {
12 3     3   38 no strict "vars";
  3         7  
  3         2909  
13             $VERSION = '0.03';
14             }
15              
16              
17             =head1 NAME
18              
19             WWW::ColiPoste - Fetch shipping status from ColiPoste
20              
21             =head1 VERSION
22              
23             Version 0.03
24              
25             =head1 SYNOPSIS
26              
27             use WWW::ColiPoste;
28              
29             my $coliposte = WWW::ColiPoste->new;
30             my $status = $coliposte->get_status(tracking_id => $id);
31              
32              
33             =head1 DESCRIPTION
34              
35             This module allows you to fetch the status of packages shipped by
36             ColiPoste, the service from the French national postal service.
37              
38             Please note that this module works by web-scrapping, and doesn't
39             do any transformation or parsing on the data apart from basic cleanup.
40             Especially, the dates and messages are as given by the web site,
41             in French.
42              
43             B Thanks to La Poste corporate thinking, this module is
44             no longer useful (since 2009), because they replaced the texts in the
45             result page with images (just in case their service was still usable).
46              
47              
48             =head1 METHODS
49              
50             =head2 new()
51              
52             Create a new objet.
53              
54             =cut
55              
56             sub new {
57 2     2 1 927 my ($class) = @_;
58              
59 2         9 my $self = bless {}, $class;
60 2         15 $self->{agent} = LWP::UserAgent->new;
61              
62 2         15926 return $self
63             }
64              
65             =head2 get_status()
66              
67             Fetch the tracking status of the given shipment ID. Returns the
68             corresponding tracking data as an array reference with the following
69             sub-structure:
70              
71             [
72             {
73             date => STRING,
74             site => STRING,
75             status => STRING,
76             },
77             ...
78             ]
79              
80             B
81              
82             =over
83              
84             =item *
85              
86             C - I<(mandatory)> tracking ID of the package
87              
88             =item *
89              
90             C - I<(optional)> source URI or file
91              
92             =item *
93              
94             C - I<(optional)> use this LWP agent or code reference for
95             fetching from the remote site
96              
97             =back
98              
99             B
100              
101             my $status = $coliposte->get_status(tracking_id => $id);
102              
103             =cut
104              
105             sub get_status {
106 7     7 1 23265 my ($self, %args) = @_;
107 7         14 my $content;
108              
109 7 50       34 exists $args{tracking_id} or croak "error: required parameter: tracking_id";
110 7         13 my $agent = $args{using};
111              
112 7 50       37 if (ref $args{from}) {
113 0         0 my $src = $args{from};
114 0         0 my $type = ref $src;
115              
116 0 0       0 if ($type eq "SCALAR") { $content = $$src }
  0 0       0  
117 0         0 elsif ($type eq "ARRAY" ) { $content = join "", @$src }
118 0         0 else { croak "error: don't know how to handle a \L$type reference" }
119             }
120             else {
121             # construct the URL
122 7   50     77 my $base_uri = $args{from}
123             || "http://www.coliposte.net/particulier/suivi_particulier.jsp?colispart=%s";
124 7         26 (my $url = $base_uri) =~ s/%s/$args{tracking_id}/;
125            
126             # fetch the content
127 7 50 0     232 if (-f $url) {
    0          
    0          
128 7         40 $content = read_file($url);
129 7         1184 $content =~ /Content-Type[^>]+charset=([^">]+)"[^>]*>/;
130 7   50     40 my $encoding = $1 || "iso-8859-1";
131 7         1106 require Encode;
132 7         12434 $content = Encode::decode($encoding, $content);
133             }
134             elsif (ref $agent eq "CODE") {
135 0         0 $content = $agent->($url)
136             }
137 0         0 elsif (ref $agent and eval { $agent->isa("LWP::UserAgent") }) {
138 0         0 $content = $agent->get($url)->decoded_content;
139             }
140             else {
141 0         0 $content = $self->{agent}->get($url)->decoded_content;
142             }
143             }
144              
145 7         2251 my $tree = HTML::TreeBuilder->new_from_content($content);
146 7         916744 my $nbsp = decode_entities(" ");
147 7         16 my @table;
148              
149 7         51 for my $table_node ($tree->look_down(_tag => "table", width => "100%")) {
150 35         19889 for my $tr_node ($table_node->look_down(_tag => "tr")) {
151 397         1102 my @row =
152 397         511 grep { !/^\s*$/ }
153 397         468 map { s/\( +/(/g; s/ +\)/)/g; $_ }
  397         723  
  397         7636  
154 397         2257 map { s/[$nbsp[:blank:][:cntrl:]]+/ /g; s/^\s*|\s*$//g; $_ }
  397         910  
  397         31482  
155 129         9882 map { $_->as_trimmed_text }
156             $tr_node->look_down(_tag => qr/^t[dh]$/);
157 129 100       882 push @table, \@row if @row;
158             }
159             }
160              
161             # remove the parts that don't interest us
162 7         29 my $filter = join "|", "Entrez ici votre", "FAQ", "Guide du site";
163 7         12 @table = grep { not $_->[0] =~ /$filter/ } @table;
  51         293  
164              
165 7         23 my @fields = qw(date status site);
166 7         11 my @status = ();
167              
168 7         29 for my $line (reverse @table[2..$#table]) {
169 9         21 push @status, { map { $fields[$_] => $line->[$_] } 0..$#fields }
  27         90  
170             }
171              
172             return \@status
173 7         1806 }
174              
175              
176             =head1 AUTHOR
177              
178             SEbastien Aperghis-Tramoni, C<< >>
179              
180             =head1 BUGS
181              
182             Please report any bugs or feature requests
183             to C, or through the web interface
184             at L.
185             I will be notified, and then you'll automatically be notified of
186             progress on your bug as I make changes.
187              
188              
189             =head1 SUPPORT
190              
191             You can find documentation for this module with the perldoc command.
192              
193             perldoc WWW::ColiPoste
194              
195             You can also look for information at:
196              
197             =over 4
198              
199             =item * RT: CPAN's request tracker
200              
201             L
202              
203             =item * AnnoCPAN: Annotated CPAN documentation
204              
205             L
206              
207             =item * CPAN Ratings
208              
209             L
210              
211             =item * Search CPAN
212              
213             L
214              
215             =back
216              
217              
218             =head1 COPYRIGHT & LICENSE
219              
220             Copyright 2008 SEbastien Aperghis-Tramoni, all rights reserved.
221              
222             This program is free software; you can redistribute it and/or modify it
223             under the same terms as Perl itself.
224              
225             =cut
226              
227             1 # End of WWW::ColiPoste