| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #--------------------------------------------------------------------- | 
| 2 |  |  |  |  |  |  | package Finance::PaycheckRecords::Fetcher; | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # Copyright 2013 Christopher J. Madsen | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # Author: Christopher J. Madsen | 
| 7 |  |  |  |  |  |  | # Created: 4 Feb 2013 | 
| 8 |  |  |  |  |  |  | # | 
| 9 |  |  |  |  |  |  | # This program is free software; you can redistribute it and/or modify | 
| 10 |  |  |  |  |  |  | # it under the same terms as Perl itself. | 
| 11 |  |  |  |  |  |  | # | 
| 12 |  |  |  |  |  |  | # This program is distributed in the hope that it will be useful, | 
| 13 |  |  |  |  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 14 |  |  |  |  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either the | 
| 15 |  |  |  |  |  |  | # GNU General Public License or the Artistic License for more details. | 
| 16 |  |  |  |  |  |  | # | 
| 17 |  |  |  |  |  |  | # ABSTRACT: Fetch paystubs from PaycheckRecords.com | 
| 18 |  |  |  |  |  |  | #--------------------------------------------------------------------- | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 1 |  |  | 1 |  | 30840 | use 5.010; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 39 |  | 
| 21 | 1 |  |  | 1 |  | 14 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 36 |  | 
| 22 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 63 |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | our $VERSION = '1.000'; | 
| 25 |  |  |  |  |  |  | # This file is part of Finance-PaycheckRecords-Fetcher 1.000 (July 12, 2014) | 
| 26 |  |  |  |  |  |  |  | 
| 27 | 1 |  |  | 1 |  | 6 | use Carp (); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 26 |  | 
| 28 | 1 |  |  | 1 |  | 1132 | use File::Slurp (); | 
|  | 1 |  |  |  |  | 19643 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 29 | 1 |  |  | 1 |  | 1127 | use LWP::UserAgent 6 ();        # SSL certificate validation | 
|  | 1 |  |  |  |  | 80378 |  | 
|  | 1 |  |  |  |  | 31 |  | 
| 30 | 1 |  |  | 1 |  | 10 | use URI (); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 14 |  | 
| 31 | 1 |  |  | 1 |  | 925 | use URI::QueryParam ();         # part of URI; has no version number | 
|  | 1 |  |  |  |  | 843 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 32 | 1 |  |  | 1 |  | 1513 | use WWW::Mechanize 1.50 ();     # autocheck on | 
|  | 1 |  |  |  |  | 177609 |  | 
|  | 1 |  |  |  |  | 12486 |  | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | #===================================================================== | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | sub new | 
| 39 |  |  |  |  |  |  | { | 
| 40 | 0 |  |  | 0 | 1 |  | my ($class, $user, $password) = @_; | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 0 |  |  |  |  |  | bless { | 
| 43 |  |  |  |  |  |  | username => $user, | 
| 44 |  |  |  |  |  |  | password => $password, | 
| 45 |  |  |  |  |  |  | mech     => WWW::Mechanize->new, | 
| 46 |  |  |  |  |  |  | }, $class; | 
| 47 |  |  |  |  |  |  | } # end new | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | #--------------------------------------------------------------------- | 
| 50 |  |  |  |  |  |  | # Get a URL, automatically supplying login credentials if needed: | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | sub _get | 
| 53 |  |  |  |  |  |  | { | 
| 54 | 0 |  |  | 0 |  |  | my ($self, $url) = @_; | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 0 |  |  |  |  |  | my $mech = $self->{mech}; | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 0 |  |  |  |  |  | $mech->get($url); | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 0 | 0 |  |  |  |  | if ($mech->form_name('Login_Form')) { | 
| 61 | 0 |  |  |  |  |  | $mech->set_fields( | 
| 62 |  |  |  |  |  |  | userStrId => $self->{username}, | 
| 63 |  |  |  |  |  |  | password  => $self->{password}, | 
| 64 |  |  |  |  |  |  | ); | 
| 65 | 0 |  |  |  |  |  | $mech->click('Login', 5, 4); | 
| 66 |  |  |  |  |  |  | # If we still see the login form, we must have failed to login properly | 
| 67 | 0 | 0 |  |  |  |  | Carp::croak("PaycheckRecords: login failed") | 
| 68 |  |  |  |  |  |  | if $mech->form_name('Login_Form'); | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  | } # end _get | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | #--------------------------------------------------------------------- | 
| 73 | 0 |  |  | 0 | 0 |  | sub listURL { 'https://www.paycheckrecords.com/in/paychecks.jsp' } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | sub available_paystubs | 
| 77 |  |  |  |  |  |  | { | 
| 78 | 0 |  |  | 0 | 1 |  | my ($self) = @_; | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 0 |  |  |  |  |  | $self->_get( $self->listURL ); | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 0 |  |  |  |  |  | my @links = $self->{mech}->find_all_links( | 
| 83 |  |  |  |  |  |  | url_regex => qr!/in/paystub_printerFriendly\.jsp! | 
| 84 |  |  |  |  |  |  | ); | 
| 85 |  |  |  |  |  |  |  | 
| 86 | 0 |  |  |  |  |  | my %stub; | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 0 |  |  |  |  |  | for my $link (@links) { | 
| 89 | 0 |  |  |  |  |  | my $url = $link->url_abs; | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 0 |  | 0 |  |  |  | $stub{ $url->query_param('date') // die "Expected date= in $url" } | 
| 92 |  |  |  |  |  |  | = $url; | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 0 |  |  |  |  |  | \%stub; | 
| 96 |  |  |  |  |  |  | } # end available_paystubs | 
| 97 |  |  |  |  |  |  | #--------------------------------------------------------------------- | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | sub mirror | 
| 101 |  |  |  |  |  |  | { | 
| 102 | 0 |  |  | 0 | 1 |  | my ($self) = @_; | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 0 |  |  |  |  |  | my $mech = $self->{mech}; | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 0 |  |  |  |  |  | my $stubs = $self->available_paystubs; | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 0 |  |  |  |  |  | my @fetched; | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 0 |  |  |  |  |  | foreach my $date (sort keys %$stubs) { | 
| 111 | 0 |  |  |  |  |  | my $fn = "Paycheck-$date.html"; | 
| 112 | 0 | 0 |  |  |  |  | next if -e $fn; | 
| 113 | 0 |  |  |  |  |  | $self->_get($stubs->{$date}); | 
| 114 | 0 |  |  |  |  |  | File::Slurp::write_file( $fn, {binmode => ':utf8'}, $mech->content ); | 
| 115 | 0 |  |  |  |  |  | push @fetched, $fn; | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 0 |  |  |  |  |  | @fetched; | 
| 119 |  |  |  |  |  |  | } # end mirror | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | #===================================================================== | 
| 122 |  |  |  |  |  |  | # Package Return Value: | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | 1; | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | __END__ |