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.105';
3 2     2   818 use strict;
  2         2  
  2         43  
4 2     2   5 use warnings;
  2         2  
  2         36  
5 2     2   31 use 5.010;
  2         5  
6 2     2   6 use Carp;
  2         1  
  2         85  
7 2     2   537 use IO::All;
  2         8262  
  2         11  
8 2     2   1485 use Text::CSV_XS;
  2         13832  
  2         84  
9 2     2   746 use Parse::SAMGov::Entity;
  2         5  
  2         56  
10 2     2   770 use Parse::SAMGov::Exclusion;
  2         3  
  2         49  
11 2     2   10 use Parse::SAMGov::Mo;
  2         3  
  2         7  
12              
13             # ABSTRACT: Parses SAM Entity Management Public Extract Layout from SAM.gov
14              
15              
16             sub parse_file {
17 3     3 1 9529 my ($self, $filename, $cb, $cb_arg) = @_;
18 3 50       38 croak "Unable to open file $filename: $!" unless -e $filename;
19 3         8 my $io = io $filename;
20 3 50       312 croak "Unable to create IO::All object for reading $filename"
21             unless defined $io;
22 3         3 my $result = [];
23 3         2 my $is_entity = 0;
24 3         4 my $entity_info = {};
25 3         10 while (my $line = $io->getline) {
26 17         1801 chomp $line;
27 17         29 $line =~ s/^\s+//g;
28 17         77 $line =~ s/\s+$//g;
29 17 50       25 next unless length $line;
30 17         49 my $obj = Parse::SAMGov::Entity->new;
31 17 100       69 if ($line =~ /BOF PUBLIC\s+(\d{8})\s+(\d{8})\s+(\d+)\s+(\d+)/) {
    100          
32 2         3 $is_entity = 1;
33 2         7 $entity_info->{date} = $1;
34 2         3 $entity_info->{rows} = $3;
35 2         4 $entity_info->{seqno} = $4;
36 2         18 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     16 or $entity_info->{seqno} ne $4);
      33        
42 2         5 last;
43             } else {
44 13 100       21 last unless $is_entity; # skip this loop and do something else
45 12         207 my @data = split /\|/x, $line;
46 12 50       41 carp "Invalid data line \n$line\n" unless $obj->load(@data);
47             }
48 12 100 66     38 if (defined $cb and ref $cb eq 'CODE') {
49 6         13 my $res = &$cb($obj, $cb_arg);
50 6 100       132 push @$result, $obj if $res;
51             } else {
52 6         26 push @$result, $obj;
53             }
54             }
55 3 100       5 unless ($is_entity) {
56 1 50       7 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         103 my $obj = Parse::SAMGov::Exclusion->new;
60 1     1   3 while (my $row = $csv->getline($io->io_handle)) {
  1         45  
  1         1  
  1         25  
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         30 push @$result, $obj;
67             }
68             }
69 1 50       89 $csv->eof or $csv->error_diag();
70             }
71 3 50       36 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.105
88              
89             =head1 SYNOPSIS
90              
91             my $parser = Parse::SAMGov->new;
92             my $entities = $parser->parse_file('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('SAM_PUBLIC_DAILY_20160701.dat',
99             sub {
100             # filter all companies with NAICS code
101             # being 541511
102             return $_[0] if exists $_[0]->NAICS->{541511};
103             return undef;
104             });
105              
106             # ... do something ...
107             my $exclusions = $parser->parse_file(exclusion => 'SAM_Exclusions_Public_Extract_16202.CSV');
108             foreach my $e (@$exclusions) {
109             ## do something with each entity that has been excluded
110             say $e->DUNS, ' has been excluded';
111             }
112              
113             =head1 METHODS
114              
115             =head2 parse_file
116              
117             This method takes as arguments the file to be parsed and returns an array
118             reference of L or L objects
119             depending on the data being parsed.
120              
121             If the second 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__