File Coverage

blib/lib/MeSH/Parser/ASCII.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             MeSH::Parser::ASCII - parser for the MeSH ASCII format
6              
7             =head1 SYNOPSIS
8              
9             use MeSH::Parser::ASCII;
10            
11             # instantiate the parser
12             my $parser = MeSH::Parser::ASCII->new( meshfile => 'd2010.bin' );
13            
14             # parse the file
15             $parser->parse();
16            
17             # loop through all the headings
18             while ( my ( $id, $heading ) = each %{ $parser->heading } ) {
19             print $id . ' - ' . $heading->{label} . "\n";
20            
21             # list synonyms
22             for my $synonym ( @{ $heading->{synonyms} } ) {
23             print "\t$synonym\n";
24             }
25            
26             # list parents
27             for my $parent ( @{ $heading->{parents} } ) {
28             print "\t" . $parent->{label} . "\n";
29             }
30             }
31              
32             =head1 DESCRIPTION
33              
34             Parser for the MeSH ASCII format.
35              
36             =over
37              
38             =item meshfile
39              
40             MeSH file in ASCII format
41              
42             =back
43              
44             =head2 METHODS
45              
46             =over
47              
48             =item parse()
49              
50             Parses the MeSH file and loads it into a hash ref.
51              
52             =item heading
53              
54             Returns a hash ref collection of all the parsed headings. Each consists of a label,
55             and id and synonyms if any were available.
56              
57             Label is extracted from I<Mesh Heading> field in Descriptor Data Elements,
58             or I<Name of substance> in Supplementary Concept Records,
59             or I<Subheading> in Qualifier Data Elements.
60              
61             Synonyms are only parsed for Descriptor Data Elements (I<PRINT ENTRY> and I<ENTRY> entries)
62              
63             =back
64              
65             =head1 AUTHOR
66              
67             Tomasz Adamusiak <tomasz@cpan.org>
68              
69             =head1 COPYRIGHT AND LICENSE
70              
71             Copyright (c) 2010 European Bioinformatics Institute. All Rights Reserved.
72              
73             This module is free software; you can redistribute it and/or modify it
74             under GPLv3.
75              
76             This software is provided "as is" without warranty of any kind.
77              
78             =cut
79              
80             package MeSH::Parser::ASCII;
81              
82 1     1   34724 use Moose 0.89;
  0            
  0            
83             use Log::Log4perl qw(:easy);
84             Log::Log4perl->easy_init( { level => $INFO, layout => '%-5p - %m%n' } );
85              
86             our $VERSION = 0.03;
87              
88             has 'meshfile' => ( is => 'rw', isa => 'Str', required => 1 );
89             has 'heading' => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
90              
91             sub parse() {
92             my $self = shift;
93              
94             INFO 'Parsing file ' . $self->meshfile . ' ...';
95              
96             # open file
97             open my $fh, '<', $self->meshfile;
98              
99             my ( $label, $id, $synonyms, $treeNos, $count );
100             $count->{syns} = 0;
101              
102             while (<$fh>) {
103              
104             # multiplatform chomp
105             # this will also rtrim the line
106             s/\s+$//;
107              
108             # initialise
109             if (/^\*NEWRECORD/) {
110             $synonyms = undef;
111             $label = undef;
112             $id = undef;
113             $treeNos = undef;
114             }
115              
116             DEBUG '<' . $_ . '>';
117              
118             # save on new line
119             if (/^$/) {
120             LOGDIE 'Could not parse heading\'s label.'
121             unless defined $label;
122             $count->{headings}++;
123             WARN "Duplicate heading found for $id"
124             if defined $self->heading->{$id};
125             $self->heading->{$id}->{label} = $label;
126             $self->heading->{$id}->{synonyms} = $synonyms
127             if defined $synonyms;
128             DEBUG $label . ' ' . $id . "\n";
129             for my $syn (@$synonyms) {
130             DEBUG "\t" . $syn;
131             $count->{syns}++;
132             }
133             $self->heading->{$id}->{treeNos} = $treeNos
134             if defined $treeNos;
135             }
136              
137             # Mesh Heading in Descriptor Data Elements
138             $label = ( split(/ = /) )[1] if /^MH = /;
139              
140             # Name of substance in Supplementary Concept Records
141             $label = ( split(/ = /) )[1] if /^NM = /;
142              
143             # Subheading in Qualifier Data Elements
144             $label = ( split(/ = /) )[1] if /^SH = /;
145              
146             $id = ( split(/ = /) )[1] if /^UI = /;
147              
148             # PRINT ENTRY and ENTRY are synonyms in Descriptor Data Elements
149             # splits on ENTRY = , and then disregards anything after pipe |
150             push @$synonyms, ( split( /\|/, ( split(/ = /) )[1] ) )[0]
151             if /^ENTRY = /;
152             push @$synonyms, ( split( /\|/, ( split(/ = /) )[1] ) )[0]
153             if /^PRINT ENTRY = /;
154              
155             # MeSH Tree Number
156             push @$treeNos, ( split(/ = /) )[1] if /^MN = /;
157             }
158             close $fh;
159              
160             LOGDIE 'Could not parse any headings.'
161             unless defined $count->{headings};
162              
163             INFO "Loaded "
164             . $count->{headings}
165             . " headings and "
166             . $count->{syns}
167             . " synonyms";
168              
169             INFO "Processing hierarchy if available";
170              
171             # construct tree
172             my $tree;
173             while ( my ( $id, $heading ) = each %{ $self->heading } ) {
174             DEBUG "No tree number in $id"
175             unless defined $heading->{treeNos};
176             for my $n ( @{ $heading->{treeNos} } ) {
177             $tree->{$n} = $heading;
178             }
179             }
180              
181             # feed back results
182             while ( my ( $id, $heading ) = each %{ $self->heading } ) {
183             for my $n ( @{ $heading->{treeNos} } ) {
184             $n =~ /^(.*)\..*$/;
185             if ( defined $1 ) {
186             my $parent = $tree->{$1};
187             if ( defined $parent ) {
188             push @{ $heading->{parents} }, $parent;
189             }
190             else {
191             WARN "No parent with tree number $n";
192             }
193             }
194             }
195             }
196              
197             1;
198             }
199              
200             1;