File Coverage

blib/lib/Data/BT/PhoneBill.pm
Criterion Covered Total %
statement 44 46 95.6
branch 5 8 62.5
condition n/a
subroutine 16 16 100.0
pod 2 2 100.0
total 67 72 93.0


line stmt bran cond sub pod time code
1             package Data::BT::PhoneBill;
2              
3             $VERSION = '1.00';
4              
5             =head1 NAME
6              
7             Data::BT::PhoneBill - Parse a BT Phone Bill from their web site
8              
9             =head1 SYNOPSIS
10              
11             my $bill = Data::BT::PhoneBill->new($filename);
12              
13             while (my $call = $bill->next_call) {
14             print $call->date, $call->time, $call->destination,
15             $call->number, $call->duration, $call->type, $call->cost;
16             }
17             }
18              
19             =head1 DESCRIPTION
20              
21             This module provides an interface for querying your BT phone bill,
22             as produced from their "View My Bill" service at http://www.bt.com/
23              
24             You should use their "Download Calls" option to save your bill as a CSV
25             file, and then feed it to this module.
26              
27             =head1 CONSTRUCTOR
28              
29             =head2 new
30              
31             my $bill = Data::BT::PhoneBill->new($filename);
32              
33             Parses the bill stored in $filename.
34              
35             =head1 FETCHING DATA
36              
37             =head2 next_call
38              
39             while (my $call = $bill->next_call) {
40             print $call->date, $call->time, $call->destination,
41             $call->number, $call->duration, $call->type, $call->cost;
42             }
43             }
44              
45             Each time you call $bill->next_call it will return a
46             Data::BT::PhoneBill::_Call object representing a telephone call (or false
47             when there are no more to read)
48              
49             Each Call object has the following methods defined:
50              
51             =head2 date
52              
53             A Date::Simple object represeting the date of the call.
54              
55             =head2 time
56              
57             A string representing the time of the call in the 24-hr format 'hh:mm'.
58              
59             =head2 destination
60              
61             A string that for local and national calls will usually be the
62             town. However this can also contain things like "Premium Rate", "Local
63             Rate" etc for 'non-geographic' calls.
64              
65             =head2 number
66              
67             A string representing the telephone number dialled, formatted as it
68             appears on the bill.
69              
70             =head2 duration
71              
72             The length of the call in seconds.
73              
74             =head2 type
75              
76             The 'type' of call - e.g. "DD Local", "DD International".
77              
78             =head2 cost
79              
80             The cost of the call, before any discounts are applied, in pence.
81              
82             =head2 chargecard
83              
84             Any chargecard number used make the call.
85              
86             =head2 installation
87              
88             The phone number from which the call was placed.
89              
90             =head2 line
91              
92             The line from which the call was placed (if a secondary line with the
93             same number is installed).
94              
95             =head2 rebate
96              
97             Any rebates applied to the call.
98              
99             =cut
100              
101 1     1   21443 use strict;
  1         4  
  1         45  
102 1     1   1326 use Text::CSV_XS;
  1         12316  
  1         405  
103 1     1   1750 use IO::File;
  1         21885  
  1         219  
104              
105             use overload
106 1         10 '<>' => \&next_call,
107 1     1   2319 fallback => 1;
  1         1863  
108              
109             sub new {
110 1     1 1 18 my ($class, $file) = @_;
111 1 50       11 my $fh = new IO::File $file, "r" or die "Can't read $file: $!\n";
112 1         141 my $headers;
113             # Downloads now have blank lines at the top
114 1         30 while (($headers = <$fh>) !~ /Date/) {
115 3 50       15 die "Couldn't find header line" if eof($fh);
116             }
117             bless {
118 1         10 _fh => $fh,
119             _parser => Text::CSV_XS->new,
120             }, $class;
121             }
122              
123 4     4   13 sub _fh { shift->{_fh} }
124 6     6   35 sub _csv { shift->{_parser} }
125              
126             sub next_call {
127 4     4 1 12125 my $self = shift;
128 4         14 my $fh = $self->_fh;
129 4         26 my $line = <$fh>;
130 4 100       23 return unless defined $line;
131 3 50       12 if ($self->_csv->parse($line)) {
132 3         146 return Data::BT::PhoneBill::_Call->new($self->_csv->fields)
133             } else {
134 0         0 warn "Cannot parse: " . $self->_csv->error_input . "\n";
135 0         0 return;
136             }
137             }
138              
139             # ==================================================================== #
140              
141             package Data::BT::PhoneBill::_Call;
142              
143 1     1   1580 use Date::Simple;
  1         7952  
  1         71  
144              
145             #ChargeCode,InstallationNo,LineNo,ChargeCardNo,Date,Time,Destination,CalledNo,Duration,TxtDirectRebate,Cost
146             our @fields = qw(type installation line chargecard _date time destination
147             _number _duration rebate _cost);
148              
149             for my $f (@fields) {
150 1     1   8 no strict 'refs';
  1         2  
  1         521  
151 22     22   1303 *{$f} = sub { shift->{$f} };
152             }
153              
154             sub new {
155 3     3   51 my ($class, @data) = @_;
156 3         12 bless { map { $fields[$_] => $data[$_] } 0..$#fields } => $class;
  33         130  
157             }
158              
159             sub date {
160 4     4   594 my @parts = split /\//, shift->_date;
161 4         29 return Date::Simple->new(@parts[2,1,0]);
162             }
163              
164             sub number {
165 3     3   13 my $num = shift->_number;
166 3         21 $num =~ s/\s+$//; $num;
  3         14  
167             }
168              
169             sub duration {
170 3     3   13 my ($h, $m, $s) = split /:/, shift->_duration;
171 3         22 return ($h * 60 * 60) + ($m * 60) + $s;
172             }
173              
174 3     3   13 sub cost { shift->_cost * 100 }
175              
176             1;
177              
178             =head1 AUTHOR
179              
180             Tony Bowden, with improvements from Simon Cozens
181              
182             =head1 BUGS and QUERIES
183              
184             Please direct all correspondence regarding this module to:
185             bug-Data-BT-PhoneBill@rt.cpan.org
186              
187             =head1 COPYRIGHT AND LICENSE
188              
189             Copyright (C) 2001-2005 Tony Bowden.
190              
191             This program is free software; you can redistribute it and/or modify
192             it under the terms of the GNU General Public License; either version
193             2 of the License, or (at your option) any later version.
194              
195             This program is distributed in the hope that it will be useful,
196             but WITHOUT ANY WARRANTY; without even the implied warranty of
197             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
198              
199             =head1 SEE ALSO
200              
201             The Perl.com article on Class::DBI is based around Data::BT::PhoneBill -
202             http://www.perl.com/pub/a/2002/11/27/classdbi.html
203              
204             =cut