File Coverage

blib/lib/PomBase/Chobo/ParseOBO.pm
Criterion Covered Total %
statement 105 122 86.0
branch 47 60 78.3
condition 14 20 70.0
subroutine 5 7 71.4
pod 0 3 0.0
total 171 212 80.6


line stmt bran cond sub pod time code
1             package PomBase::Chobo::ParseOBO;
2              
3             =head1 NAME
4              
5             PomBase::Chobo::ParseOBO - Parse the bits of an OBO file needed for
6             loading Chado
7              
8             =head1 SYNOPSIS
9              
10             =head1 AUTHOR
11              
12             Kim Rutherford C<< >>
13              
14             =head1 BUGS
15              
16             Please report any bugs or feature requests to C.
17              
18             =head1 SUPPORT
19              
20             You can find documentation for this module with the perldoc command.
21              
22             perldoc PomBase::Chobo::ParseOBO
23              
24             =over 4
25              
26             =back
27              
28             =head1 COPYRIGHT & LICENSE
29              
30             Copyright 2012 Kim Rutherford, all rights reserved.
31              
32             This program is free software; you can redistribute it and/or modify it
33             under the same terms as Perl itself.
34              
35             =head1 FUNCTIONS
36              
37             =cut
38              
39             our $VERSION = '0.039'; # VERSION
40              
41 4     4   255741 use Mouse;
  4         87956  
  4         21  
42 4     4   3651 use FileHandle;
  4         34751  
  4         22  
43              
44 4     4   3066 use PomBase::Chobo::OntologyData;
  4         29  
  4         5922  
45              
46             sub die_line
47             {
48 0     0 0 0 my $filename = shift;
49 0         0 my $linenum = shift;
50 0         0 my $message = shift;
51              
52 0         0 die "$filename:$linenum:$message\n";
53             }
54              
55             sub _finish_stanza
56             {
57 72     72   123 my $filename = shift;
58 72         100 my $current = shift;
59 72         138 my $terms_ref = shift;
60 72         95 my $metadata_ref = shift;
61              
62 72 50 66     185 if ($current->{is_obsolete} && $current->{is_relationshiptype}) {
63 0         0 return;
64             }
65              
66 72 50       164 if (!defined $current->{id}) {
67 0         0 die_line $filename, $current->{line}, "stanza has no id\n";
68 0         0 return;
69             }
70              
71 72 100       170 if ($current->{is_obsolete}) {
72 4         9 delete $current->{alt_id};
73             }
74              
75 72         150 $current->{metadata} = $metadata_ref;
76 72         149 $current->{source_file} = $filename;
77 72   100     322 $current->{relationship} //= [];
78              
79 72         119 my $namespace_from_metadata = 0;
80              
81 72 100       169 if (!defined $current->{namespace}) {
82             $current->{namespace} =
83             $metadata_ref->{'default-namespace'} //
84             $metadata_ref->{'ontology'} //
85 34   66     131 $current->{source_file} . '::' . $current->{id} =~ s/:.*//r;
      33        
86              
87 34 100       108 if ($current->{namespace} eq 'ro') {
88 4         9 $current->{namespace} = 'relations';
89             }
90              
91 34         65 $namespace_from_metadata = 1;
92             }
93              
94 72 100       152 if ($current->{is_a}) {
95             map {
96 54         72 push @{$current->{relationship}},
  54         224  
97             {
98             'relationship_name' => 'is_a',
99             'other_term' => $_,
100             };
101 47         89 } @{$current->{is_a}};
  47         126  
102              
103 47         110 delete $current->{is_a};
104             }
105              
106 72 100       158 if ($current->{synonym}) {
107 35         82 my %seen_synonyms = ();
108              
109             $current->{synonym} = [
110             map {
111              
112 60         122 my $seen_synonym = $seen_synonyms{$_->{synonym}};
113 60 100 66     168 if ($seen_synonym && lc $seen_synonym->{scope} eq 'exact') {
114             # keep it
115             } else {
116 58         240 $seen_synonyms{$_->{synonym}} = $_;
117             }
118 35         62 } @{$current->{synonym}}
  35         86  
119             ];
120              
121 35         191 $current->{synonym} = [sort { $a->{synonym} cmp $b->{synonym} } values %seen_synonyms];
  30         89  
122             }
123              
124 72         157 my $options = { namespace_from_metadata => $namespace_from_metadata };
125              
126 72         296 my $new_term = PomBase::Chobo::OntologyTerm->make_object($current, $options);
127              
128 72         201 push @$terms_ref, $new_term;
129             }
130              
131             sub fatal
132             {
133 0     0 0 0 my $message = shift;
134              
135 0         0 die "fatal: $message\n";
136             }
137              
138             my %interesting_metadata = (
139             'default-namespace' => 1,
140             'ontology' => 1,
141             'date' => 1,
142             'data-version' => 1,
143             );
144              
145             sub parse
146             {
147 11     11 0 3856 my $self = shift;
148 11         73 my %args = @_;
149              
150 11         29 my $filename = $args{filename};
151 11 50       39 if (!defined $filename) {
152 0         0 die 'no filename passed to parse()';
153             }
154              
155 11         25 my $ontology_data = $args{ontology_data};
156 11 50       30 if (!defined $ontology_data) {
157 0         0 die 'no ontology_data passed to parse()';
158             }
159              
160 11         45 my %metadata = ();
161 11         23 my @terms = ();
162              
163 11         21 my $current = undef;
164 11         21 my @synonyms = ();
165              
166 11         17 my %meta = ();
167              
168 11 50       585 open my $fh, '<:utf8', $filename or die "can't open $filename: $!";
169              
170 11         47 my $line_number = 0;;
171              
172 11         437 while (defined (my $line = <$fh>)) {
173 821         1240 $line_number++;
174 821         1247 chomp $line;
175 821         2197 $line =~ s/![^"\n]*$//;
176 821         2563 $line =~ s/\s+$//;
177 821         1456 $line =~ s/^\s+//;
178              
179 821 100       1980 next if length $line == 0;
180              
181 747 100       1612 if ($line =~ /^\[(.*)\]$/) {
182 72         168 my $stanza_type = $1;
183              
184 72 100       143 if (defined $current) {
185 61         171 _finish_stanza($filename, $current, \@terms, \%metadata);
186             }
187              
188 72         116 my $is_relationshiptype = 0;
189              
190 72 100       153 if ($stanza_type eq 'Typedef') {
191 10         17 $is_relationshiptype = 1;
192             } else {
193 62 50       128 if ($stanza_type ne 'Term') {
194 0         0 die "unknown stanza type '[$stanza_type]'\n";
195             }
196             }
197 72         358 $current = {
198             is_relationshiptype => $is_relationshiptype,
199             source_file_line_number => $line_number,
200             };
201             } else {
202 675 100       1082 if ($current) {
203 578         1633 my @bits = split /: /, $line, 2;
204 578 50       1095 if (@bits == 2) {
205 578         871 my $field_name = $bits[0];
206 578         763 my $field_value = $bits[1];
207              
208             # ignored for now
209 578         748 my $modifier_string;
210              
211 578 100       1159 if ($field_value =~ /\}$/) {
212 2         18 $field_value =~ s/(.*)\{(.*)\}$/$1/;
213 2         16 $modifier_string = $2;
214 2         17 $field_value =~ s/\s+$//;
215             }
216              
217 578         1232 my $field_conf = $PomBase::Chobo::OntologyConf::field_conf{$field_name};
218              
219 578 100       1365 if (defined $field_conf) {
220 443 100       887 if (defined $field_conf->{process}) {
221 214         314 eval {
222 214         598 $field_value = $field_conf->{process}->($field_value);
223             };
224 214 50       454 if ($@) {
225 0         0 warn qq(warning "$@" at $filename line $.\n);
226             }
227             }
228 443 100       737 if (defined $field_value) {
229 442 100 100     1763 if (defined $field_conf->{type} &&
      66        
230             ($field_conf->{type} eq 'SINGLE' || $field_conf->{type} eq 'SINGLE_HASH')) {
231 260         1362 $current->{$field_name} = $field_value;
232             } else {
233 182         257 push @{$current->{$field_name}}, $field_value;
  182         1156  
234             }
235             }
236             }
237             } else {
238 0         0 die "can't parse line - no colon: $line\n";
239             }
240             } else {
241             # we're parsing metadata
242 97 50       335 if ($line =~ /^(.+?):\s*(.*)/) {
243 97         287 my ($key, $value) = ($1, $2);
244              
245 97 100       372 if ($interesting_metadata{$key}) {
246 34 50       76 if (defined $metadata{$key}) {
247 0         0 warn qq(metadata key "$key" occurs more than once in header\n);
248             } else {
249 34         145 $metadata{$key} = $value;
250             }
251             }
252             } else {
253 0         0 fatal "can't parse header line: $line";
254             }
255             }
256             }
257             }
258              
259 11 50       48 if (defined $current) {
260 11         48 _finish_stanza($filename, $current, \@terms, \%metadata);
261             }
262              
263 11 50       174 close $fh or die "can't close $filename: $!";
264              
265 11         32 eval {
266 11         68 $ontology_data->add(metadata => \%metadata,
267             terms => \@terms);
268             };
269 11 50       167 if ($@) {
270 0           die "failed while reading $filename: $@\n";
271             }
272             }
273              
274             1;