File Coverage

blib/lib/Parse/SAMGov.pm
Criterion Covered Total %
statement 66 69 95.6
branch 22 34 64.7
condition 5 12 41.6
subroutine 11 11 100.0
pod 1 1 100.0
total 105 127 82.6


line stmt bran cond sub pod time code
1             package Parse::SAMGov;
2             $Parse::SAMGov::VERSION = '0.104';
3 2     2   796 use strict;
  2         2  
  2         43  
4 2     2   6 use warnings;
  2         2  
  2         34  
5 2     2   30 use 5.010;
  2         4  
6 2     2   6 use Carp;
  2         2  
  2         80  
7 2     2   468 use IO::All;
  2         8384  
  2         11  
8 2     2   1432 use Text::CSV_XS;
  2         13291  
  2         77  
9 2     2   749 use Parse::SAMGov::Entity;
  2         5  
  2         54  
10 2     2   742 use Parse::SAMGov::Exclusion;
  2         3  
  2         42  
11 2     2   9 use Parse::SAMGov::Mo;
  2         2  
  2         6  
12              
13             # ABSTRACT: Parses SAM Entity Management Public Extract Layout from SAM.gov
14              
15              
16             sub parse_file {
17 3     3 1 47788 my ($self, $filename, $cb, $cb_arg) = @_;
18 3 50       42 croak "Unable to open file $filename: $!" unless -e $filename;
19 3         8 my $io = io $filename;
20 3 50       313 croak "Unable to create IO::All object for reading $filename"
21             unless defined $io;
22 3         5 my $result = [];
23 3         4 my $is_entity = 0;
24 3         3 my $entity_info = {};
25 3         10 while (my $line = $io->getline) {
26 17         1751 chomp $line;
27 17         28 $line =~ s/^\s+//g;
28 17         74 $line =~ s/\s+$//g;
29 17 50       29 next unless length $line;
30 17         49 my $obj = Parse::SAMGov::Entity->new;
31 17 100       67 if ($line =~ /BOF PUBLIC\s+(\d{8})\s+(\d{8})\s+(\d+)\s+(\d+)/) {
    100          
32 2         3 $is_entity = 1;
33 2         5 $entity_info->{date} = $1;
34 2         3 $entity_info->{rows} = $3;
35 2         3 $entity_info->{seqno} = $4;
36 2         13 next;
37             } elsif ($line =~ /EOF\s+PUBLIC\s+(\d{8})\s+(\d{8})\s+(\d+)\s+(\d+)/) {
38             croak "Invalid footer q{$line} in file"
39             if ( $entity_info->{date} ne $1
40             or $entity_info->{rows} ne $3
41 2 50 33     20 or $entity_info->{seqno} ne $4);
      33        
42 2         4 last;
43             } else {
44 13 100       21 last unless $is_entity; # skip this loop and do something else
45 12         208 my @data = split /\|/x, $line;
46 12 50       36 carp "Invalid data line \n$line\n" unless $obj->load(@data);
47             }
48 12 100 66     37 if (defined $cb and ref $cb eq 'CODE') {
49 6         12 my $res = &$cb($obj, $cb_arg);
50 6 100       139 push @$result, $obj if $res;
51             } else {
52 6         18 push @$result, $obj;
53             }
54             }
55 3 100       7 unless ($is_entity) {
56 1 50       8 my $csv = Text::CSV_XS->new({ binary => 1 })
57             or croak "Failed to create Text::CSV_XS object: "
58             . Text::CSV_XS->error_diag();
59 1         104 my $obj = Parse::SAMGov::Exclusion->new;
60 1     1   3 while (my $row = $csv->getline($io->io_handle)) {
  1         52  
  1         1  
  1         24  
61 4 50       236 carp "Invalid data line \n$row\n" unless $obj->load(@$row);
62 4 50 33     11 if (defined $cb and ref $cb eq 'CODE') {
63 0         0 my $res = &$cb($obj, $cb_arg);
64 0 0       0 push @$result, $obj if $res;
65             } else {
66 4         28 push @$result, $obj;
67             }
68             }
69 1 50       62 $csv->eof or $csv->error_diag();
70             }
71 3 50       34 return $result if scalar @$result;
72 0         0 return;
73             }
74              
75             1;
76              
77             =pod
78              
79             =encoding UTF-8
80              
81             =head1 NAME
82              
83             Parse::SAMGov - Parses SAM Entity Management Public Extract Layout from SAM.gov
84              
85             =head1 VERSION
86              
87             version 0.104
88              
89             =head1 SYNOPSIS
90              
91             my $parser = Parse::SAMGov::Parser->new;
92             my $entities = $parser->parse_file(entity => 'SAM_PUBLIC_DAILY_20160701.dat');
93             foreach my $e (@$entities) {
94             ## do something with each entity
95             say $e->DUNS, ' is a valid entity';
96             }
97             #... use in filter mode like grep ...
98             my $entities_541511 = $parser->parse_file(entity =>
99             'SAM_PUBLIC_DAILY_20160701.dat',
100             sub {
101             # filter all companies with NAICS code
102             # being 541511
103             return $_[0] if exists $_[0]->NAICS->{541511};
104             return undef;
105             });
106              
107             # ... do something ...
108             my $exclusions = $parser->parse_file(exclusion => 'SAM_Exclusions_Public_Extract_16202.CSV');
109             foreach my $e (@$exclusions) {
110             ## do something with each entity that has been excluded
111             say $e->DUNS, ' has been excluded';
112             }
113              
114             =head1 METHODS
115              
116             =head2 parse_file
117              
118             This method takes as arguments the file to be parsed and returns an array
119             reference of L or L objects
120             depending on the data being parsed. Returns undef if the type is not 'entity' or
121             'exclusion'. If the third argument is a coderef then passes each Entity or
122             Exclusion object into the callback where the user can select which objects they
123             want to return. The user has to return 1 if they want the object returned in the
124             array ref or undef if they do not.
125              
126             my $entities = $parser->parse_file('SAM_PUBLIC_DAILY_20160701.dat');
127             my $exclusions = $parser->parse_file('SAM_Exclusions_Public_Extract_16202.CSV');
128             my $entities = $parser->parse_file('SAM_PUBLIC_DAILY_20160701.dat', sub {
129             my ($entity_or_exclusion, $optional_user_arg) = @_;
130             #... do something ...
131             return 1 if (!$entity_or_exclusion->is_private);
132             return undef;
133             }, $optional_user_arg);
134              
135             =head1 SEE ALSO
136              
137             L and L for the object
138             definitions.
139              
140             =head1 AUTHOR
141              
142             Vikas N Kumar
143              
144             =head1 COPYRIGHT AND LICENSE
145              
146             This software is copyright (c) 2016 by Selective Intellect LLC.
147              
148             This is free software; you can redistribute it and/or modify it under
149             the same terms as the Perl 5 programming language system itself.
150              
151             =cut
152              
153             __END__