# Copyright 2008-2010 Tim Rayner # # This file is part of Bio::MAGETAB. # # Bio::MAGETAB is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Bio::MAGETAB is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Bio::MAGETAB. If not, see . # # $Id: IDF.pm 340 2010-07-23 13:19:27Z tfrayner $ package Bio::MAGETAB::Util::Writer::IDF; use Moose; use MooseX::FollowPBP; use Carp; use List::Util qw( max ); BEGIN { extends 'Bio::MAGETAB::Util::Writer::Tabfile' }; has 'magetab_object' => ( is => 'ro', isa => 'Bio::MAGETAB::Investigation', required => 1 ); sub _collapse_contacts { my ( $self, @contacts ) = @_; # Given a list of Contact objects, collapse them into a list of # arrayrefs suitable for passing to $self->_write_line() # This is just a convenience hash to store data my %list = ( 'value' => [ 'Person Roles' ], 'termSource' => [ 'Person Roles Term Source REF' ], 'accession' => [ 'Person Roles Term Accession Number' ], ); foreach my $contact ( @contacts ) { # Multiple Role terms can be specified, separated by semicolons. push @{ $list{'value'} }, ( join(';', map { $_->get_value() } $contact->get_roles() ) || q{} ); # Unfortunately as of the v1.1 specification, multiple # TermSources and Accessions are a no-no. That makes this part # a bit more complicated than it needs to be. my (%ts_test, %acc_test, $ts1, $acc1); foreach my $role ( $contact->get_roles() ) { my $ts = $role->get_termSource(); my $acc = $role->get_accession(); # Only store the first one of each. $ts1 ||= $ts; $acc1 ||= $acc; $ts_test{ $ts } = $ts if $ts; $acc_test{ $acc } = $acc if $acc; } # Warn where the model isn't quite in line with the spec. if ( ( scalar grep { defined $_ } values %ts_test ) > 1 ) { carp("Warning: Multiple Role Term Sources (unsupported by MAGE-TAB format)."); } if ( ( scalar grep { defined $_ } values %acc_test ) > 1 ) { carp("Warning: Multiple Role Term Accessions (unsupported by MAGE-TAB format)."); } # Just output the first TermSource and/or Accession we encountered. push @{ $list{'termSource'} }, ( $ts1 ? $ts1->get_name() : q{} ); # Skip accessions for MAGE-TAB v1.0 export. if ( $self->get_export_version ne '1.0' ) { push @{ $list{'accession'} }, ( $acc1 || q{} ); } } # This will be the eventual output order of the lines. return ( $list{'value'}, $list{'termSource'}, $list{'accession'} ); } sub _get_thing_type { my ( $self, $thing ) = @_; my $type; if ( UNIVERSAL::can( $thing, 'get_type' ) ) { $type = $thing->get_type(); } elsif ( UNIVERSAL::can( $thing, 'get_status' ) ) { $type = $thing->get_status(); } elsif ( UNIVERSAL::can( $thing, 'get_factorType' ) ) { $type = $thing->get_factorType(); } elsif ( UNIVERSAL::can( $thing, 'get_protocolType' ) ) { $type = $thing->get_protocolType(); } else { confess("Error: Cannot find a ControlledVocab-linked attribute for " . blessed $thing ); } return $type; } sub _get_thing_type_value { my ( $self, $thing ) = @_; my $type = $self->_get_thing_type( $thing ); return $type ? $type->get_value() : q{}; } sub _get_thing_type_accession { my ( $self, $thing ) = @_; # Return undef if we're exporting MAGE-TAB v1.0. return if ( $self->get_export_version() eq '1.0' ); my $type = $self->_get_thing_type( $thing ); return $type ? $type->get_accession() : q{}; } sub _get_thing_type_termsource_name { my ( $self, $thing ) = @_; my $type = $self->_get_thing_type( $thing ); return $self->_get_type_termsource_name($type); } sub _get_thing_accession { my ( $self, $thing ) = @_; # Return undef if we're exporting MAGE-TAB v1.0. return if ( $self->get_export_version() eq '1.0' ); return $thing->get_accession(); } sub write { my ( $self ) = @_; my $inv = $self->get_magetab_object(); my %single = ( 'Investigation Title' => 'title', 'Experiment Description' => 'description', 'Date of Experiment' => 'date', 'Public Release Date' => 'publicReleaseDate', ); # FIXME check these field names against the spec! my @other_comments; my %multi = ( 'contacts' => [ sub { return ( [ 'Person Last Name', map { $_->get_lastName() } @_ ] ) }, sub { return ( [ 'Person First Name', map { $_->get_firstName() } @_ ] ) }, sub { return ( [ 'Person Mid Initials', map { $_->get_midInitials() } @_ ] ) }, sub { return ( [ 'Person Email', map { $_->get_email() } @_ ] ) }, sub { return ( [ 'Person Affiliation', map { $_->get_organization() } @_ ] ) }, sub { return ( [ 'Person Phone', map { $_->get_phone() } @_ ] ) }, sub { return ( [ 'Person Fax', map { $_->get_fax() } @_ ] ) }, sub { return ( [ 'Person Address', map { $_->get_address() } @_ ] ) }, sub { $self->_collapse_contacts( @_ ); }, sub { push @other_comments, map { $_->get_comments() } @_ }, ], 'factors' => [ sub { return ( [ 'Experimental Factor Name', map { $_->get_name() } @_ ] ) }, sub { return ( [ 'Experimental Factor Type', map { $self->_get_thing_type_value($_) } @_ ] ) }, sub { return ( [ 'Experimental Factor Term Source REF', map { $self->_get_thing_type_termsource_name($_) } @_ ] ) }, sub { return ( [ 'Experimental Factor Term Accession Number', map { $self->_get_thing_type_accession($_) } @_ ] ) }, ], 'sdrfs' => [ sub { return ( [ 'SDRF File', map { $_->get_uri() } @_ ] ) }, ], 'protocols' => [ sub { return ( [ 'Protocol Name', map { $_->get_name() } @_ ] ) }, sub { return ( [ 'Protocol Description', map { $_->get_text() } @_ ] ) }, sub { return ( [ 'Protocol Software', map { $_->get_software() } @_ ] ) }, sub { return ( [ 'Protocol Hardware', map { $_->get_hardware() } @_ ] ) }, sub { return ( [ 'Protocol Contact', map { $_->get_contact() } @_ ] ) }, sub { return ( [ 'Protocol Type', map { $self->_get_thing_type_value($_) } @_ ] ) }, sub { return ( [ 'Protocol Term Source REF', map { $self->_get_thing_type_termsource_name($_) } @_ ] ) }, sub { return ( [ 'Protocol Term Accession Number', map { $self->_get_thing_type_accession($_) } @_ ] ) }, ], 'publications' => [ sub { return ( [ 'Publication Title', map { $_->get_title() } @_ ] ) }, sub { return ( [ 'Publication Author List', map { $_->get_authorList() } @_ ] ) }, sub { return ( [ 'PubMed ID', map { $_->get_pubMedID() } @_ ] ) }, sub { return ( [ 'Publication DOI', map { $_->get_DOI() } @_ ] ) }, sub { return ( [ 'Publication Status', map { $self->_get_thing_type_value($_) } @_ ] ) }, sub { return ( [ 'Publication Status Term Source REF', map { $self->_get_thing_type_termsource_name($_) } @_ ] ) }, sub { return ( [ 'Publication Status Term Accession Number', map { $self->_get_thing_type_accession($_) } @_ ] ) }, ], 'termSources' => [ sub { return ( [ 'Term Source Name', map { $_->get_name() } @_ ] ) }, sub { return ( [ 'Term Source Version', map { $_->get_version() } @_ ] ) }, sub { return ( [ 'Term Source File', map { $_->get_uri() } @_ ] ) }, ], 'designTypes' => [ sub { return ( [ 'Experimental Design', map { $_->get_value() } @_ ] ) }, sub { return ( [ 'Experimental Design Term Accession Number', map { $self->_get_thing_accession($_) } @_ ] ) }, sub { return ( [ 'Experimental Design Term Source REF', map { $self->_get_type_termsource_name($_) } @_ ] ) }, ], 'normalizationTypes' => [ sub { return ( [ 'Normalization Type', map { $_->get_value() } @_ ] ) }, sub { return ( [ 'Normalization Term Accession Number', map { $self->_get_thing_accession($_) } @_ ] ) }, sub { return ( [ 'Normalization Term Source REF', map { $self->_get_type_termsource_name($_) } @_ ] ) }, ], 'replicateTypes' => [ sub { return ( [ 'Replicate Type', map { $_->get_value() } @_ ] ) }, sub { return ( [ 'Replicate Term Accession Number', map { $self->_get_thing_accession($_) } @_ ] ) }, sub { return ( [ 'Replicate Term Source REF', map { $self->_get_type_termsource_name($_) } @_ ] ) }, ], 'qualityControlTypes' => [ sub { return ( [ 'Quality Control Type', map { $_->get_value() } @_ ] ) }, sub { return ( [ 'Quality Control Term Accession Number', map { $self->_get_thing_accession($_) } @_ ] ) }, sub { return ( [ 'Quality Control Term Source REF', map { $self->_get_type_termsource_name($_) } @_ ] ) }, ], ); # We want a regular table, so figure out how many columns we will # need. Note that we need a minimum of 2 columns to accommodate the single fields. my @objcounts = map { my $getter = "get_$_"; scalar @{ [ $inv->$getter ] }; } keys %multi; $self->set_num_columns( max( 1 + max @objcounts, 2 ) ); # Introduce a Version tag (new in v1.1). unless ( $self->get_export_version() eq '1.0' ) { $self->_write_line( 'MAGE-TAB Version', '1.1' ); } # Single elements are straightforward. while ( my ( $field, $value ) = each %single ) { my $getter = "get_$value"; my $value = $inv->$getter; if ( defined $value && $value ne q{} ) { $self->_write_line( $field, $value ); } } # All the complicated stuff gets handled by the dispatch methods # in %multi. ATTR: while ( my ( $field, $subs ) = each %multi ) { my $getter = "get_$field"; my @attrs = $inv->$getter; next ATTR if ( scalar @attrs == 1 && ! defined $attrs[0] ); foreach my $sub ( @$subs ) { LINEREF: foreach my $lineref ( $sub->( @attrs ) ) { next LINEREF unless ref $lineref eq 'ARRAY'; # Don't write the line if there's nothing to write but the tag. if ( scalar grep { defined $_ && $_ ne q{} } @{ $lineref }[1..$#$lineref] ) { $self->_write_line( @{ $lineref } ); } } } } # All comments on IDF-related classes are dumped into the IDF at # the end. FIXME consider maybe inserting them at the appropriate # places? Subsequent parsing won't preserve these locations though. foreach my $comment ( $inv->get_comments(), @other_comments ) { my $field = sprintf("Comment[%s]", $comment->get_name()); $self->_write_line( $field, $comment->get_value() ); } } # Make the classes immutable. In theory this speeds up object # instantiation for a small compilation time cost. __PACKAGE__->meta->make_immutable(); no Moose; =head1 NAME Bio::MAGETAB::Util::Writer::IDF - Export of MAGE-TAB Investigation objects. =head1 SYNOPSIS use Bio::MAGETAB::Util::Writer::IDF; my $writer = Bio::MAGETAB::Util::Writer::IDF->new({ magetab_object => $investigation, filehandle => $idf_fh, }); $writer->write(); =head1 DESCRIPTION Export of Investigations to IDF files. =head1 ATTRIBUTES See the L class for superclass attributes. =over 2 =item magetab_object The Bio::MAGETAB::Investigation to export. This is a required attribute. =back =head1 METHODS =over 2 =item write Exports the Investigation to IDF. =back =head1 SEE ALSO L L =head1 AUTHOR Tim F. Rayner =head1 LICENSE This library is released under version 2 of the GNU General Public License (GPL). =cut 1;