File Coverage

blib/lib/OWL/Simple/OBOWriter.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             OWL::Simple::OBOWriter - a simple OWL to OBO converter
6              
7             =head1 SYNOPSIS
8              
9             use OWL::Simple::Parser;
10             use OWL::Simple::OBOWriter;
11            
12             # load Experimental Factor Ontology
13             my $parser = OWL::Simple::Parser->new( owlfile => 'efo.owl' );
14             my $writer = OWL::Simple::OBOWriter->new( owlparser => $parser );
15            
16             # convert the ontology to OBO and save in current directory
17             $writer->write();
18              
19             =head1 DESCRIPTION
20              
21             A simple OWL to OBO converter.
22              
23             In the constructor you only need to pass an OWL::Simple::Parser object.
24             All other arguments are optional:
25              
26             =over
27              
28             =item outputfile
29              
30             Defaults to simple-owl-obowriter-output.obo.
31              
32             =item version
33              
34             Version of the ontology to record in the OBO file.
35              
36             =item namespace
37              
38             Specifies the default namespace
39              
40             =back
41              
42             =head2 METHODS
43              
44             =over
45              
46             =item write()
47              
48             Converts and writes the file in current directory
49              
50             =back
51              
52             =head1 AUTHOR
53              
54             Tomasz Adamusiak <tomasz@cpan.org>
55              
56             =head1 COPYRIGHT AND LICENSE
57              
58             Copyright (c) 2010 European Bioinformatics Institute. All Rights Reserved.
59              
60             This module is free software; you can redistribute it and/or modify it
61             under GPLv3.
62              
63             This software is provided "as is" without warranty of any kind.
64              
65             =cut
66              
67             package OWL::Simple::OBOWriter;
68              
69 1     1   2209 use Moose 0.89;
  0            
  0            
70             use OWL::Simple::Parser 0.10;
71             use Log::Log4perl qw(:easy);
72             use XML::Parser 2.34;
73             Log::Log4perl->easy_init( { level => $INFO, layout => '%-5p - %m%n' } );
74              
75             our $VERSION = 0.06;
76              
77             has 'owlparser' => ( is => 'rw', isa => 'OWL::Simple::Parser', required => 1 );
78             has 'outputfile' =>
79             ( is => 'rw', isa => 'Str', default => 'simple-owl-obowriter-output.obo' );
80             has 'version' => ( is => 'rw', isa => 'Str', required => 0 );
81             has 'namespace' => ( is => 'rw', isa => 'Str', required => 0 );
82              
83             sub BUILD() {
84             my $self = shift;
85             $self->parse_owl();
86             }
87              
88             sub write() {
89             my $self = shift;
90              
91             $self->write_header();
92              
93             $self->write_terms();
94            
95             $self->write_typedefs();
96              
97             INFO 'Converted ' . $self->owlparser->owlfile . ' to ' . $self->outputfile;
98              
99             1;
100             }
101              
102             # Writes OBO header
103              
104             sub write_header() {
105             my $self = shift;
106             my $parser = $self->owlparser;
107            
108             open my $fh, '>:utf8', $self->outputfile or LOGCROAK $!;
109             {
110             local $\ = "\n"; # do the magic of println
111             print $fh 'format-version: 1.2';
112             if (defined $self->version){
113             print $fh "data-version: " . $self->version;
114             } else {
115             print $fh "data-version: " . $parser->version;
116             }
117             print $fh "date: " . datetime();
118             print $fh "auto-generated-by: OWL::Simple::OBOWriter $VERSION";
119             print $fh "default-namespace: " . $self->namespace
120             if defined $self->namespace;
121              
122             #print $fh "idspace: efo http://www.ebi.ac.uk/efo";
123             }
124             close $fh;
125             DEBUG "WROTE HEADER";
126             }
127              
128             # Writes OBO footer containing Typedef stanzas
129              
130             sub write_typedefs() {
131             my $self = shift;
132             open my $fh, '>>:utf8', $self->outputfile or LOGCROAK $!;
133             {
134             local $\ = "\n"; # do the magic of println
135             print $fh q{};
136             print $fh '[Typedef]'; # term stanza
137             print $fh 'id: part_of';
138             print $fh 'name: part_of';
139             }
140             close $fh;
141             DEBUG "WROTE TYPEDEFS";
142             }
143              
144             # Initiates the OWL parser.
145              
146             sub parse_owl() {
147             my $self = shift;
148             my $parser = $self->owlparser;
149             $parser->parse();
150             }
151              
152             sub cleanup_id_for_OLS($) {
153             my $s = shift;
154             $s =~ s!http://www.ebi.ac.uk/efo/!!;
155             $s =~ s!http://purl.org/obo/owl/.*#!!;
156             $s =~ s!http://purl.obolibrary.org/obo/!!;
157             $s =~ s!\Qhttp://www.ebi.ac.uk/chebi/searchId.do;?chebiId=\E!!;
158             $s =~ s!\Qhttp://www.ebi.ac.uk/chebi/searchId.do?chebiId=\E!!;
159             $s =~ s!http://www.ifomis.org/bfo/.*/snap#!snap:!;
160             $s =~ s!http://www.ifomis.org/bfo/.*/span#!span:!;
161             $s =~ s!\Qhttp://www.geneontology.org/formats/oboInOwl#\E!oboInOwl:!;
162             # required for ensembl consumption
163             $s =~ s!_!:!g;
164             return $s;
165             }
166              
167             # Writes out owl classes.
168             sub write_terms($) {
169             my $self = shift;
170             my $parser = $self->owlparser;
171             open my $fh, '>>:utf8', $self->outputfile or LOGCROAK $!;
172              
173             for my $key ( sort ( keys %{ $parser->class } ) ) {
174             my $term = $parser->class->{$key};
175             $key = cleanup_id_for_OLS($key);
176              
177             # there's no obsolete parent in OBO
178             next if $key eq 'oboInOwl:ObsoleteClass';
179            
180             # skip unlaballed artefacts
181             unless (defined $term->label){
182             WARN "SKIPPING $key DUE TO UNDEFINED LABEL";
183             next;
184             }
185             # process stanza
186             local $\ = "\n"; # do the magic of println
187             print $fh q{};
188             print $fh '[Term]';
189             print $fh 'id: ' . $key . ' ! ' . $term->label;
190             print $fh 'name: ' . $term->label;
191              
192             # write definition (there can be only 0 or 1)
193             print $fh 'def: "' . escape_chars( $term->definitions->[0] ) . '" []'
194             if defined $term->definitions->[0];
195              
196             # write synonyms
197             for my $synonym ( @{ $term->synonyms } ) {
198             print $fh 'synonym: "' . escape_chars($synonym) . '" EXACT []';
199             }
200              
201             # write xrefs
202             for my $xref ( @{ $term->xrefs } ) {
203             $xref = cleanup_id_for_OLS( escape_chars($xref) );
204             print $fh 'xref: ' . $xref;
205             }
206              
207             # write isa_s
208             for my $isa ( @{ $term->subClassOf } ) {
209             $isa = cleanup_id_for_OLS($isa);
210             if ( $isa eq 'http://www.w3.org/2002/07/owl#Thing' ) {
211             INFO 'Skipping owl#Thing on ' . $key . ' ! ' . $term->label;;
212             }
213             elsif ( $isa eq 'oboInOwl:ObsoleteClass' ) {
214             # should not have any other relations
215             WARN 'obsolete term ' . $key . ' with multiple is_a relations'
216             if scalar @{ $term->subClassOf } > 1;
217             print $fh 'is_obsolete: true';
218             last;
219             }
220             else {
221             print $fh 'is_a: ' . $isa;
222             }
223              
224             }
225              
226             # write relationships
227             for my $part_of ( @{ $term->part_of } ) {
228             $part_of = cleanup_id_for_OLS($part_of);
229             # FIXME need to fix circular references
230             # FIXME warn if exists on an obsolete term
231             # print $fh 'relationship: part_of ' . $part_of;
232             }
233             }
234              
235             close $fh;
236             DEBUG "WROTE TERMS";
237             }
238              
239             # Supplies a date in an OBO required format.
240              
241             sub datetime() {
242             my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
243             localtime(time);
244             return sprintf "%02d:%02d:%4d %02d:%02d", $mday, $mon + 1, $year + 1900,
245             $hour, $min;
246             }
247              
248             # Escape chars in synonyms and definitions.
249              
250             sub escape_chars($) {
251             my $s = shift;
252             $s =~ s/\n//g;
253             $s =~ s!\\!\\\\!g;
254             $s =~ s/\[/\\\[/g;
255             $s =~ s/\]/\\\]/g;
256              
257             # OBO edit seems to complain about these
258             #$s =~ s/\)/\\\)/g;
259             #$s =~ s/\(/\\\(/g;
260             $s =~ s/\{/\\\{/g;
261             $s =~ s/\}/\\\}/g;
262             $s =~ s/\t/ /g;
263             $s =~ s!,! !;
264             $s =~ s/"//g;
265             return $s;
266             }
267              
268             1;