File Coverage

blib/lib/PomBase/Chobo/ParseOBO.pm
Criterion Covered Total %
statement 116 137 84.6
branch 55 70 78.5
condition 14 20 70.0
subroutine 5 7 71.4
pod 0 3 0.0
total 190 237 80.1


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.041'; # VERSION
40              
41 5     5   637512 use Mouse;
  5         137570  
  5         27  
42 5     5   5591 use FileHandle;
  5         45126  
  5         40  
43              
44 5     5   5208 use PomBase::Chobo::OntologyData;
  5         63  
  5         10742  
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 81     81   127 my $filename = shift;
58 81         124 my $current = shift;
59 81         119 my $terms_ref = shift;
60 81         109 my $metadata_ref = shift;
61              
62 81 50 66     251 if ($current->{is_obsolete} && $current->{is_relationshiptype}) {
63 0         0 return;
64             }
65              
66 81 50       213 if (!defined $current->{name}) {
67 0         0 warn "term without a name tag ignored: ", $current->{id}, "\n";
68 0         0 return;
69             }
70              
71 81 50       195 if (!defined $current->{id}) {
72 0         0 die_line $filename, $current->{line}, "stanza has no id\n";
73 0         0 return;
74             }
75              
76 81 100       275 if ($current->{id} !~ /:/) {
77             # try to find a sensible ID if the ID looks like a name ("id: output_of")
78 12 100       36 if ($current->{xref}) {
79 6         11 for my $xref (@{$current->{xref}}) {
  6         58  
80 6 100       26 if ($xref =~ /^RO:/) {
81 2         7 $current->{id} = $xref;
82             goto ID_FIXED
83 2         23 }
84             }
85              
86 4         8 for my $xref (@{$current->{xref}}) {
  4         9  
87 4 50       14 if ($xref =~ /^BFO:/) {
88 0         0 $current->{id} = $xref;
89             goto ID_FIXED
90 0         0 }
91             }
92             }
93             }
94              
95             ID_FIXED:
96 81 100       200 if ($current->{is_obsolete}) {
97 6         19 delete $current->{alt_id};
98             }
99              
100 81         171 $current->{metadata} = $metadata_ref;
101 81         181 $current->{source_file} = $filename;
102 81   100     454 $current->{relationship} //= [];
103              
104 81         128 my $namespace_from_metadata = 0;
105              
106 81 100       176 if (!defined $current->{namespace}) {
107             $current->{namespace} =
108             $metadata_ref->{'default-namespace'} //
109             $metadata_ref->{'ontology'} //
110 40   66     162 $current->{source_file} . '::' . $current->{id} =~ s/:.*//r;
      33        
111              
112 40 100       137 if ($current->{namespace} eq 'ro') {
113 4         10 $current->{namespace} = 'relations';
114             }
115              
116 40         61 $namespace_from_metadata = 1;
117             }
118              
119 81 100       171 if ($current->{is_a}) {
120             map {
121 55         77 push @{$current->{relationship}},
  55         263  
122             {
123             'relationship_name' => 'is_a',
124             'other_term' => $_,
125             };
126 48         67 } @{$current->{is_a}};
  48         137  
127              
128 48         121 delete $current->{is_a};
129             }
130              
131 81 100       175 if ($current->{synonym}) {
132 37         76 my %seen_synonyms = ();
133              
134             $current->{synonym} = [
135             map {
136              
137 66         147 my $seen_synonym = $seen_synonyms{$_->{synonym}};
138 66 100 66     160 if ($seen_synonym && lc $seen_synonym->{scope} eq 'exact') {
139             # keep it
140             } else {
141 63         228 $seen_synonyms{$_->{synonym}} = $_;
142             }
143 37         76 } @{$current->{synonym}}
  37         88  
144             ];
145              
146 37         329 $current->{synonym} = [sort { $a->{synonym} cmp $b->{synonym} } values %seen_synonyms];
  34         104  
147             }
148              
149 81         237 my $options = { namespace_from_metadata => $namespace_from_metadata };
150              
151 81         411 my $new_term = PomBase::Chobo::OntologyTerm->make_object($current, $options);
152              
153 81         320 push @$terms_ref, $new_term;
154             }
155              
156             sub fatal
157             {
158 0     0 0 0 my $message = shift;
159              
160 0         0 die "fatal: $message\n";
161             }
162              
163             my %interesting_metadata = (
164             'default-namespace' => 1,
165             'ontology' => 1,
166             'date' => 1,
167             'data-version' => 1,
168             );
169              
170             sub parse
171             {
172 13     13 0 5514 my $self = shift;
173 13         65 my %args = @_;
174              
175 13         35 my $filename = $args{filename};
176 13 50       46 if (!defined $filename) {
177 0         0 die 'no filename passed to parse()';
178             }
179              
180 13         31 my $ontology_data = $args{ontology_data};
181 13 50       39 if (!defined $ontology_data) {
182 0         0 die 'no ontology_data passed to parse()';
183             }
184              
185 13         30 my %metadata = ();
186 13         23 my @terms = ();
187              
188 13         24 my $current = undef;
189 13         107 my @synonyms = ();
190              
191 13         25 my %meta = ();
192              
193 13 50       942 open my $fh, '<:utf8', $filename or die "can't open $filename: $!";
194              
195 13         43 my $line_number = 0;;
196              
197 13         777 while (defined (my $line = <$fh>)) {
198 899         1239 $line_number++;
199 899         1411 chomp $line;
200 899         1749 $line =~ s/![^"\n]*$//;
201 899         2865 $line =~ s/\s+$//;
202 899         1627 $line =~ s/^\s+//;
203              
204 899 100       2296 next if length $line == 0;
205              
206 812 100       1722 if ($line =~ /^\[(.*)\]$/) {
207 81         188 my $stanza_type = $1;
208              
209 81 100       184 if (defined $current) {
210 68         237 _finish_stanza($filename, $current, \@terms, \%metadata);
211             }
212              
213 81         159 my $is_relationshiptype = 0;
214              
215 81 100       211 if ($stanza_type eq 'Typedef') {
216 14         30 $is_relationshiptype = 1;
217             } else {
218 67 50       155 if ($stanza_type ne 'Term') {
219 0         0 die "unknown stanza type '[$stanza_type]'\n";
220             }
221             }
222 81         466 $current = {
223             is_relationshiptype => $is_relationshiptype,
224             source_file_line_number => $line_number,
225             };
226             } else {
227 731 100       1275 if ($current) {
228 622         1693 my @bits = split /: /, $line, 2;
229 622 50       1434 if (@bits == 2) {
230 622         915 my $field_name = $bits[0];
231 622         839 my $field_value = $bits[1];
232              
233             # ignored for now
234 622         758 my $modifier_string;
235              
236 622 100       1207 if ($field_value =~ /\}$/) {
237 2         15 $field_value =~ s/(.*)\{(.*)\}$/$1/;
238 2         6 $modifier_string = $2;
239 2         11 $field_value =~ s/\s+$//;
240             }
241              
242 622         1323 my $field_conf = $PomBase::Chobo::OntologyConf::field_conf{$field_name};
243              
244 622 100       1426 if (defined $field_conf) {
245 487 100       1012 if (defined $field_conf->{process}) {
246 240         367 eval {
247 240         655 $field_value = $field_conf->{process}->($field_value);
248             };
249 240 50       492 if ($@) {
250 0         0 warn qq(warning "$@" at $filename line $.\n);
251             }
252             }
253 487 100       856 if (defined $field_value) {
254 486 100 100     2057 if (defined $field_conf->{type} &&
      66        
255             ($field_conf->{type} eq 'SINGLE' || $field_conf->{type} eq 'SINGLE_HASH')) {
256 294         1740 $current->{$field_name} = $field_value;
257             } else {
258 192         307 push @{$current->{$field_name}}, $field_value;
  192         1715  
259             }
260             }
261             }
262             } else {
263 0         0 die "can't parse line - no colon: $line\n";
264             }
265             } else {
266             # we're parsing metadata
267 109 50       404 if ($line =~ /^(.+?):\s*(.*)/) {
268 109         393 my ($key, $value) = ($1, $2);
269              
270 109 100       439 if ($interesting_metadata{$key}) {
271 40 50       108 if (defined $metadata{$key}) {
272 0         0 warn qq(metadata key "$key" occurs more than once in header\n);
273             } else {
274 40         183 $metadata{$key} = $value;
275             }
276             }
277             } else {
278 0         0 fatal "can't parse header line: $line";
279             }
280             }
281             }
282             }
283              
284 13 50       43 if (defined $current) {
285 13         55 _finish_stanza($filename, $current, \@terms, \%metadata);
286             }
287              
288 13 50       276 close $fh or die "can't close $filename: $!";
289              
290 13         28 eval {
291 13         88 $ontology_data->add(metadata => \%metadata,
292             terms => \@terms);
293             };
294 13 50       173 if ($@) {
295 0           die "failed while reading $filename: $@\n";
296             }
297             }
298              
299             1;