File Coverage

blib/lib/Text/Corpus/Inspec.pm
Criterion Covered Total %
statement 32 199 16.0
branch 0 74 0.0
condition 0 12 0.0
subroutine 10 20 50.0
pod 4 5 80.0
total 46 310 14.8


line stmt bran cond sub pod time code
1             package Text::Corpus::Inspec;
2              
3 1     1   39337 use strict;
  1         2  
  1         44  
4 1     1   6 use warnings;
  1         1  
  1         31  
5 1     1   1019 use File::Spec::Functions;
  1         1095  
  1         107  
6 1     1   7 use File::Find;
  1         2  
  1         161  
7 1     1   32183 use Log::Log4perl;
  1         149558  
  1         10  
8 1     1   1150 use Text::Corpus::Inspec::Document;
  1         4  
  1         1075  
9 1     1   5829 use Data::Dump qw(dump);
  1         37530  
  1         107  
10              
11             BEGIN
12             {
13 1     1   13 use Exporter ();
  1         2  
  1         33  
14 1     1   8 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         3  
  1         163  
15 1     1   4 $VERSION = '1.00';
16 1         23 @ISA = qw(Exporter);
17 1         4 @EXPORT = qw();
18 1         3 @EXPORT_OK = qw();
19 1         2945 %EXPORT_TAGS = ();
20             }
21              
22             #12345678901234567890123456789012345678901234
23             #Interface to Inspec abstracts corpus.
24              
25             =head1 NAME
26              
27             C - Interface to Inspec abstracts corpus.
28              
29             =head1 SYNOPSIS
30              
31             use Text::Corpus::Inspec;
32             use Data::Dump qw(dump);
33             use Log::Log4perl qw(:easy);
34             Log::Log4perl->easy_init ($INFO);
35             my $corpus = Text::Corpus::Inspec->new (corpusDirectory => $corpusDirectory);
36             dump $corpus->getTotalDocuments;
37              
38             =head1 DESCRIPTION
39              
40             C provides a simple interface for accessing the documents
41             in the Inspec corpus.
42              
43             The categories, description, title, etc... of a specified document
44             are accessed using L. Also, all errors and
45             warnings are logged using L, which should be L.
46              
47             =head1 CONSTRUCTOR
48              
49             =head2 C
50              
51             The method C creates an instance of the C class with the following
52             parameters:
53              
54             =over
55              
56             =item C
57              
58             corpusDirectory => '...'
59              
60             C is the path to the top most directory of the corpus documents. It is
61             the path to the directory containing the sub-directories named C, C,
62             and C of the corpus and is needed to locate all the documents in the corpus.
63             If it is not defined, then the enviroment variable
64             C is used if it is defined.
65             A message is logged and an exception is thrown if no directory is specified.
66              
67             =back
68              
69             =cut
70              
71             sub new
72             {
73             # create the class object.
74 0     0 1   my ($Class, %Parameters) = @_;
75 0   0       my $Self = bless {}, ref($Class) || $Class;
76              
77             # set the names of the document set subset types.
78 0           $Self->{test} = 'test';
79 0           $Self->{training} = 'training';
80 0           $Self->{validation} = 'validation';
81 0           $Self->{all} = 'all';
82              
83             # set the corpusDirectory.
84 0 0         unless (exists ($Parameters{corpusDirectory}))
85             {
86 0 0 0       if (defined (%ENV) && exists ($ENV{TEXT_CORPUS_INSPEC_CORPUSDIRECTORY}))
87             {
88 0           $Parameters{corpusDirectory} = $ENV{TEXT_CORPUS_INSPEC_CORPUSDIRECTORY};
89             }
90             }
91              
92             # got to at least have corpusDirectory defined.
93 0 0         unless (exists($Parameters{corpusDirectory}))
94             {
95 0           my $logger = Log::Log4perl->get_logger();
96 0           $logger->logdie ("corpusDirectory not defined.\n");
97             }
98              
99             # corpusDirectory must be a directory.
100 0 0         unless (-d $Parameters{corpusDirectory})
101             {
102 0           my $logger = Log::Log4perl->get_logger();
103 0           $logger->logdie("base directory for corpus, '" . $Parameters{corpusDirectory} . "', not a directory or does not exist: " . $!);
104             }
105              
106             # find all the corpus files in the base directory.
107 0           $Self->_computeListOfCorpusFiles(corpusDirectory => $Parameters{corpusDirectory});
108              
109 0           return $Self;
110             }
111              
112             =head1 METHODS
113              
114             =head2 C
115              
116             getDocument (index => $index, subsetType => $subsetType)
117              
118             C returns a L object of the document with the specified C
119             and C, which is either C<'all'>, C<'test'>, C<'training'>,
120             or C<'validation'>; the default is C<'all'>.
121              
122             getDocument (uri => $uri)
123              
124             C returns a L object of the document with specified C.
125              
126             =cut
127              
128             sub getDocument
129             {
130 0     0 1   my ($Self, %Parameters) = @_;
131              
132             # get the subsetType parameter.
133 0           my $subsetType = $Self->{all};
134 0 0         $subsetType = lc $Parameters{subsetType} if exists $Parameters{subsetType};
135              
136             # get the index if defined.
137 0           my $indexOfDocument;
138 0 0         $indexOfDocument = abs $Parameters{index} if exists $Parameters{index};
139              
140             # compute the path from the index or uri if defined.
141 0           my $documentPath;
142 0 0         $documentPath = $Self->getFilePathOfDocument ($indexOfDocument, $subsetType) if defined $indexOfDocument;
143 0           my $corpusFileHash = $Self->{corpusFileHash};
144 0 0 0       $documentPath = $Parameters{uri} if exists ($Parameters{uri}) && exists $corpusFileHash->{$Parameters{uri}};
145              
146             # if we have no path defined, return undef.
147 0 0         return undef unless defined $documentPath;
148              
149             # get the document object.
150 0           my $document;
151             eval
152 0           {
153 0           $document = Text::Corpus::Inspec::Document->new (filename => $documentPath);
154             };
155 0 0         if ($@)
156             {
157 0           my $logger = Log::Log4perl->get_logger();
158 0           $logger->logwarn( "caught exception, probably parsing error in file '"
159             . $documentPath . "', skipping the file: " . $@);
160 0           $document = undef;
161             }
162 0           return $document;
163             }
164              
165             =head2 C
166              
167             getTotalDocuments (subsetType => 'all')
168              
169             C returns the total number of documents in the specified subset-type
170             of the corpus; which is either C<'all'>, C<'test'>, C<'training'>,
171             or C<'validation'>; the default is C<'all'>. The index to the
172             documents in each subset ranges from zero to C $subsetType) - 1>.
173              
174             =cut
175              
176             # returns the total number of documents in the corpus.
177             sub getTotalDocuments
178             {
179 0     0 1   my ($Self, %Parameters) = @_;
180              
181             # get the type of the document set whose size is to be returned.
182 0           my $subsetType = $Self->{all};
183 0 0         $subsetType = lc $Parameters{subsetType} if exists $Parameters{subsetType};
184 0 0         $subsetType = $Self->{all} unless defined $subsetType;
185              
186             # return the document set size.
187 0 0         return $#{$Self->{testDocuments}} + 1 if ($subsetType eq $Self->{test});
  0            
188 0 0         return $#{$Self->{trainingDocuments}} + 1 if ($subsetType eq $Self->{training});
  0            
189 0 0         return $#{$Self->{validationDocuments}} + 1 if ($subsetType eq $Self->{validation});
  0            
190 0           return $#{$Self->{testDocuments}} + $#{$Self->{trainingDocuments}} + $#{$Self->{validationDocuments}} + 3;
  0            
  0            
  0            
191             }
192              
193             # returns the path of a file given an index and type.
194             sub getFilePathOfDocument
195             {
196 0     0 0   my ($Self, @Parameters) = @_;
197              
198             # get the parameters.
199 0           my $type = $Self->{all};
200 0           my $indexOfDocument = 0;
201              
202 0           foreach my $parameter (@Parameters)
203             {
204 0 0         if ($parameter =~ m/^\d+$/)
205             {
206 0           $indexOfDocument = $parameter;
207             }
208             else
209             {
210 0           $type = lc $parameter;
211             }
212             }
213              
214             # force $indexOfDocument to a valid range given the document type.
215 0           my $totalDocuments = $Self->getTotalDocuments (subsetType => $type);
216 0 0         $indexOfDocument = 0 if ($indexOfDocument < 0);
217 0 0         $indexOfDocument = $totalDocuments - 1 if ($indexOfDocument >= $totalDocuments);
218              
219             # return the file path of the document given the data type specified.
220 0 0         if ($type eq $Self->{test})
    0          
    0          
221             {
222 0           return $Self->{testDocuments}->[$indexOfDocument];
223             }
224             elsif ($type eq $Self->{training})
225             {
226 0           return $Self->{trainingDocuments}->[$indexOfDocument];
227             }
228             elsif ($type eq $Self->{validation})
229             {
230 0           return $Self->{validationDocuments}->[$indexOfDocument];
231             }
232             else
233             {
234 0           foreach my $type ($Self->{test}, $Self->{training}, $Self->{validation})
235             {
236 0 0         return $Self->getFilePathOfDocument ($indexOfDocument, $type) if ($indexOfDocument < $Self->getTotalDocuments (subsetType => $type));
237 0           $indexOfDocument -= $Self->getTotalDocuments (subsetType => $type);
238             }
239             }
240             }
241              
242             # find all the corpus files in the base directory.
243             {
244             my @listOfCorpuseFiles;
245              
246             sub _computeListOfCorpusFiles
247             {
248 0     0     my ($Self, %Parameters) = @_;
249 0           @listOfCorpuseFiles = ();
250 0           find(\&_storeCorpusFiles, $Parameters{corpusDirectory});
251 0           @listOfCorpuseFiles = sort @listOfCorpuseFiles;
252 0           my %corpusFileHash;
253 0           for (my $i = 0; $i < @listOfCorpuseFiles; $i++)
254             {
255 0           $corpusFileHash{$listOfCorpuseFiles[$i]} = $i;
256             }
257 0           $Self->{corpusFileList} = \@listOfCorpuseFiles;
258 0           $Self->{corpusFileHash} = \%corpusFileHash;
259 0           $Self->_buildListsOfCorpusFiles ();
260             }
261              
262             sub _storeCorpusFiles
263             {
264 0 0 0 0     if ((-e $File::Find::name) && ($File::Find::name =~ /\d+\.abstr$/))
265             {
266 0           push @listOfCorpuseFiles, $File::Find::name;
267             }
268             }
269             }
270              
271              
272             # build the list of all the files in the corpus.
273             sub _buildListsOfCorpusFiles
274             {
275 0     0     my ($Self, %Parameters) = @_;
276              
277             # separate the files into Test, Training, Validation.
278 0           my (@test, @training, @validation);
279              
280 0           my $corpusFileList = $Self->{corpusFileList};
281 0           foreach my $filePath (@$corpusFileList)
282             {
283 0 0         if ($filePath =~ m/Test.\d+\.abstr$/)
    0          
    0          
284             {
285 0           push @test, $filePath;
286             }
287             elsif ($filePath =~ m/Training.\d+\.abstr$/)
288             {
289 0           push @training, $filePath;
290             }
291             elsif ($filePath =~ m/Validation.\d+\.abstr$/)
292             {
293 0           push @validation, $filePath;
294             }
295             }
296              
297 0           $Self->{testDocuments} = $Self->_getDocumentList (fileList => \@test);
298 0           $Self->{trainingDocuments} = $Self->_getDocumentList (fileList => \@training);
299 0           $Self->{validationDocuments} = $Self->_getDocumentList (fileList => \@validation);
300             }
301              
302              
303             sub _getDocumentList
304             {
305 0     0     my ($Self, %Parameters) = @_;
306              
307             # build the list of files associated with each document.
308 0           my %newFileList;
309 0           foreach my $filename (@{$Parameters{fileList}})
  0            
310             {
311             # split off the file number.
312 0 0         if ($filename =~ /(\d+)\.abstr$/)
313             {
314 0           $newFileList{$1 + 0} = $filename;
315             }
316             }
317              
318             # get the sorted list of document numbers.
319 0           my @docNumbers = sort {$a <=> $b} keys %newFileList;
  0            
320              
321             # convert the list of documents to an array.
322 0           my @documentList;
323 0           for (my $i = 0; $i < @docNumbers; $i++)
324             {
325 0           $documentList[$i] = $newFileList{$docNumbers[$i]};
326             }
327              
328 0           return \@documentList;
329             }
330              
331              
332             sub _testDocumentPath
333             {
334 0     0     my $Self = shift;
335 0           my $totalTest = $Self->getTotalDocuments (subsetType => 'test');
336 0           my $totalTraining = $Self->getTotalDocuments (subsetType => 'training');
337 0           my $totalValidation = $Self->getTotalDocuments (subsetType => 'validation');
338              
339 0           my $offset = 0;
340 0           for (my $i = 0; $i < $totalTest; $i++)
341             {
342 0 0         return 0 if ($Self->getFilePathOfDocument($i, 'test') ne $Self->getFilePathOfDocument($offset + $i));
343             }
344              
345 0           $offset += $totalTest;
346 0           for (my $i = 0; $i < $totalTraining; $i++)
347             {
348 0 0         return 0 if ($Self->getFilePathOfDocument($i, 'training') ne $Self->getFilePathOfDocument($offset + $i));
349             }
350              
351 0           $offset += $totalTraining;
352 0           for (my $i = 0; $i < $totalValidation; $i++)
353             {
354 0 0         return 0 if ($Self->getFilePathOfDocument($i, 'validation') ne $Self->getFilePathOfDocument($offset + $i));
355             }
356              
357 0           return 1;
358             }
359              
360             =head2 C
361              
362             test ()
363              
364             C does tests to ensure the documents in the corpus are accessible
365             and can be parsed. It returns true if all tests pass, otherwise a
366             description of the test that failed is logged using L and
367             false is returned.
368              
369             For example:
370              
371             use Text::Corpus::Inspec;
372             use Data::Dump qw(dump);
373             use Log::Log4perl qw(:easy);
374             Log::Log4perl->easy_init ($INFO);
375             my $corpus = Text::Corpus::Inspec->new (corpusDirectory => $corpusDirectory);
376             dump $corpus->test;
377              
378             =cut
379              
380             sub test
381             {
382 0     0 1   my ($Self, %Parameters) = @_;
383              
384             # make sure a corpus file list created.
385 0 0         unless ($Self->_testDocumentPath)
386             {
387 0           my $logger = Log::Log4perl->get_logger();
388 0           $logger->logwarn ("list of corpus files corrupt; was corpusDirectory defined correctly?\n");
389 0           return 0;
390             }
391              
392             # make sure some files were found.
393 0           my $totalDocuments = $Self->getTotalDocuments ();
394 0 0         unless ($totalDocuments)
395             {
396 0           my $logger = Log::Log4perl->get_logger();
397 0           $logger->logwarn ("list of corpus files is empty; was corpusDirectory defined correctly?\n");
398 0           return 0;
399             }
400              
401             # test for missing files.
402 0           my $numberOfFilesToTest = 20;
403 0           my @listOfMissingFiles;
404 0           my $corpusFileList = $Self->{corpusFileList};
405 0           for (my $i = 0; $i < $numberOfFilesToTest; $i++)
406             {
407 0           my $index = int rand scalar @$corpusFileList;
408 0           my $filePath = $Self->getFilePathOfDocument($index);
409 0 0         push @listOfMissingFiles, $filePath unless -f $filePath;
410             }
411              
412             # make sure some of the files exist.
413 0 0         if (@listOfMissingFiles)
414             {
415 0           my $logger = Log::Log4perl->get_logger();
416 0           my $totalMissingFiles = scalar @listOfMissingFiles;
417 0           my $message = "randomly selected $numberOfFilesToTest files fron list of corpus files and found\n$totalMissingFiles of them are missing:\n";
418 0           $message .= join ("\n", @listOfMissingFiles, "was corpusDirectory defined correctly?\n");
419 0           $logger->logwarn ($message);
420 0           return 0;
421             }
422              
423             # randomly test some of the files for parsing errors.
424 0           for (my $i = 0; $i < $numberOfFilesToTest; $i++)
425             {
426 0           my $index = int rand scalar $totalDocuments;
427 0           my $totalTextInfo = 0;
428             eval
429 0           {
430 0           my $document = $Self->getDocument(index => $index);
431 0 0         next unless defined $document;
432 0           my %documentInfo;
433 0           $documentInfo{title} = $document->getTitle();
434 0           $documentInfo{body} = $document->getBody();
435 0           $documentInfo{content} = $document->getContent();
436 0           $documentInfo{categories} = $document->getCategories();
437 0           $documentInfo{uri} = [$document->getUri()];
438 0           foreach my $key (keys %documentInfo)
439             {
440 0           $totalTextInfo += @{$documentInfo{$key}};
  0            
441             }
442             };
443 0 0         if ($@)
444             {
445 0           my $logger = Log::Log4perl->get_logger();
446 0           my $totalMissingFiles = scalar @listOfMissingFiles;
447 0           my $message = "parsing of one of the corpus documents through an exception: $@\n";
448 0           $logger->logwarn ($message);
449 0           return 0;
450             }
451 0 0         if ($totalTextInfo < 2)
452             {
453 0           my $logger = Log::Log4perl->get_logger();
454 0           my $totalMissingFiles = scalar @listOfMissingFiles;
455 0           my $message = "parsing of one of the corpus documents returned insufficient information.\nwas corpusDirectory defined correctly?\n";
456 0           $logger->logwarn ($message);
457 0           return 0;
458             }
459             }
460              
461 0           return 1;
462             }
463              
464             =head1 EXAMPLES
465              
466             The example below will print out all the information for each document in the corpus.
467              
468             use Text::Corpus::Inspec;
469             use Data::Dump qw(dump);
470             use Log::Log4perl qw(:easy);
471             Log::Log4perl->easy_init ($INFO);
472             my $corpus = Text::Corpus::Inspec->new (corpusDirectory => $corpusDirectory);
473             my $totalDocuments = $corpus->getTotalDocuments ();
474             for (my $i = 0; $i < $totalDocuments; $i++)
475             {
476             eval
477             {
478             my $document = $corpus->getDocument (index => $i);
479             my %documentInfo;
480             $documentInfo{title} = $document->getTitle ();
481             $documentInfo{body} = $document->getBody ();
482             $documentInfo{content} = $document->getContent ();
483             $documentInfo{categories} = $document->getCategories ();
484             $documentInfo{uri} = $document->getUri ();
485             dump \%documentInfo;
486             };
487             }
488              
489             =head1 INSTALLATION
490              
491             To install the module set the environment variable
492             C to the path of the
493             Inspec corpus and run the following commands:
494              
495             perl Makefile.PL
496             make
497             make test
498             make install
499              
500             If you are on a windows box you should use 'nmake' rather than 'make'.
501              
502             The module will install if C is not defined, but
503             little testing will be performed. After the Inspec corpus is installed testing
504             of the module can be performed by running:
505              
506             use Text::Corpus::Inspec;
507             use Data::Dump qw(dump);
508             use Log::Log4perl qw(:easy);
509             Log::Log4perl->easy_init ($INFO);
510             my $corpus = Text::Corpus::Inspec->new (corpusDirectory => $corpusDirectory);
511             dump $corpus->test;
512              
513             =head1 AUTHOR
514              
515             Jeff Kubina
516              
517             =head1 COPYRIGHT
518              
519             Copyright (c) 2009 Jeff Kubina. All rights reserved.
520             This program is free software; you can redistribute
521             it and/or modify it under the same terms as Perl itself.
522              
523             The full text of the license can be found in the
524             LICENSE file included with this module.
525              
526             =head1 KEYWORDS
527              
528             inspec, english corpus, information processing
529              
530             =head1 SEE ALSO
531              
532             L, L
533              
534             =cut
535              
536             1;
537             # The preceding line will help the module return a true value