Mercurial > repos > cpt > cpt_psm_plotter
comparison lib/CPT/GBK2GFF3.pm @ 1:8691c1c61a8e draft default tip
planemo upload commit 94b0cd1fff0826c6db3e7dc0c91c0c5a8be8bb0c
| author | cpt |
|---|---|
| date | Mon, 05 Jun 2023 02:48:47 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:54c7a3ea81e2 | 1:8691c1c61a8e |
|---|---|
| 1 package CPT::GBK2GFF3; | |
| 2 use strict; | |
| 3 use warnings; | |
| 4 use CPT; | |
| 5 use Data::Dumper; | |
| 6 use autodie; | |
| 7 use Bio::SeqIO; | |
| 8 use Moose; | |
| 9 | |
| 10 has data => ( | |
| 11 is => 'rw', | |
| 12 isa => 'ArrayRef', | |
| 13 default => sub { [] }, | |
| 14 ); | |
| 15 has header => ( | |
| 16 is => 'rw', | |
| 17 isa => 'ArrayRef', | |
| 18 ); | |
| 19 has sequence => ( | |
| 20 is => 'rw', | |
| 21 isa => 'Str', | |
| 22 ); | |
| 23 has seqid => ( | |
| 24 is => 'rw', | |
| 25 isa => 'Str', | |
| 26 ); | |
| 27 has genbank => ( | |
| 28 is => 'rw', | |
| 29 isa => 'Str', | |
| 30 ); | |
| 31 has is_circular => ( | |
| 32 is => 'rw', | |
| 33 isa => 'Str', | |
| 34 ); | |
| 35 has id_prefix => ( is => 'rw', isa => 'Str' ); | |
| 36 has global_feat_idx => ( is => 'rw', isa => 'Int', default => sub { 0 } ); | |
| 37 has source => ( is => 'rw', isa => 'ArrayRef'); | |
| 38 | |
| 39 my %feat_type_count; | |
| 40 my %reserved_keys = map { $_ => 1 } | |
| 41 qw/ID Name Alias Parent Target Gap Derives_from Note Dbxref Ontology_term/; | |
| 42 | |
| 43 use CPT::Bio; | |
| 44 my $bio = CPT::Bio->new(); | |
| 45 my %key_mapping = ( | |
| 46 "-" => [ "located_sequence_feature", "SO:0000110" ], | |
| 47 "-10_signal" => [ "minus_10_signal", "SO:0000175" ], | |
| 48 "-35_signal" => [ "minus_35_signal", "SO:0000176" ], | |
| 49 "3'UTR" => [ "three_prime_UTR", "SO:0000205" ], | |
| 50 "3'clip" => [ "three_prime_clip", "SO:0000557" ], | |
| 51 "5'UTR" => [ "five_prime_UTR", "SO:0000204" ], | |
| 52 "5'clip" => [ "five_prime_clip", "SO:0000555" ], | |
| 53 "CAAT_signal" => [ "CAAT_signal", "SO:0000172" ], | |
| 54 "CDS" => [ "CDS", "SO:0000316" ], | |
| 55 "D-loop" => [ "D_loop", "SO:0000297" ], | |
| 56 "D_segment" => [ "D_gene", "SO:0000458" ], | |
| 57 "GC_signal" => [ "GC_rich_region", "SO:0000173" ], | |
| 58 "LTR" => [ "long_terminal_repeat", "SO:0000286" ], | |
| 59 "RBS" => [ "ribosome_entry_site", "SO:0000139" ], | |
| 60 "STS" => [ "STS", "SO:0000331" ], | |
| 61 "TATA_signal" => [ "TATA_box", "SO:0000174" ], | |
| 62 "attenuator" => [ "attenuator", "SO:0000140" ], | |
| 63 "enhancer" => [ "enhancer", "SO:0000165" ], | |
| 64 "exon" => [ "exon", "SO:0000147" ], | |
| 65 "gap" => [ "gap", "SO:0000730" ], | |
| 66 "gene" => [ "gene", "SO:0000704" ], | |
| 67 "iDNA" => [ "iDNA", "SO:0000723" ], | |
| 68 "intron" => [ "intron", "SO:0000188" ], | |
| 69 "mRNA" => [ "mRNA", "SO:0000234" ], | |
| 70 "mat_peptide" => [ "mature_protein_region", "SO:0000419" ], | |
| 71 "misc_RNA" => [ "transcript", "SO:0000673" ], | |
| 72 "misc_binding" => [ "binding_site", "SO:0000409" ], | |
| 73 "misc_difference" => [ "sequence_difference", "SO:0000413" ], | |
| 74 "misc_feature" => [ "region", "SO:0000001" ], | |
| 75 "misc_recomb" => [ "recombination_feature", "SO:0000298" ], | |
| 76 "misc_signal" => [ "regulatory_region", "SO:0005836" ], | |
| 77 "misc_structure" => [ "sequence_secondary_structure", "SO:0000002" ], | |
| 78 "modified_base" => [ "modified_DNA_base", "SO:0000305" ], | |
| 79 "operon" => [ "operon", "SO:0000178" ], | |
| 80 "oriT" => [ "origin_of_transfer", "SO:0000724" ], | |
| 81 "polyA_signal" => [ "polyA_signal_sequence", "SO:0000551" ], | |
| 82 "polyA_site" => [ "polyA_site", "SO:0000553" ], | |
| 83 "precursor_RNA" => [ "primary_transcript", "SO:0000185" ], | |
| 84 "prim_transcript" => [ "primary_transcript", "SO:0000185" ], | |
| 85 "primer_bind" => [ "primer_binding_site", "SO:0005850" ], | |
| 86 "promoter" => [ "promoter", "SO:0000167" ], | |
| 87 "protein_bind" => [ "protein_binding_site", "SO:0000410" ], | |
| 88 "rRNA" => [ "rRNA", "SO:0000252" ], | |
| 89 "repeat_region" => [ "repeat_region", "SO:0000657" ], | |
| 90 "repeat_unit" => [ "repeat_unit", "SO:0000726" ], | |
| 91 "satellite" => [ "satellite_DNA", "SO:0000005" ], | |
| 92 "scRNA" => [ "scRNA", "SO:0000013" ], | |
| 93 "sig_peptide" => [ "signal_peptide", "SO:0000418" ], | |
| 94 "snRNA" => [ "snRNA", "SO:0000274" ], | |
| 95 "snoRNA" => [ "snoRNA", "SO:0000275" ], | |
| 96 "source" => [ "contig", "SO:0000149" ], # manually modified | |
| 97 "stem_loop" => [ "stem_loop", "SO:0000313" ], | |
| 98 "tRNA" => [ "tRNA", "SO:0000253" ], | |
| 99 "terminator" => [ "terminator", "SO:0000141" ], | |
| 100 "transit_peptide" => [ "transit_peptide", "SO:0000725" ], | |
| 101 "variation" => [ "sequence_variant", "SO:0000109" ], | |
| 102 ); | |
| 103 | |
| 104 | |
| 105 sub get_gff3_file { | |
| 106 my ($self) = @_; | |
| 107 my @output; | |
| 108 for my $header_line ( @{ $self->header() } ) { | |
| 109 push( @output, $header_line ); | |
| 110 } | |
| 111 push( @output, join( "\t", @{ $self->get_source } ) ); | |
| 112 for my $data_line ( @{ $self->data() }) { | |
| 113 push( @output, join( "\t", @{$data_line} ) ); | |
| 114 } | |
| 115 push( @output, '##FASTA' ); | |
| 116 push( @output, '>' . $self->seqid() ); | |
| 117 my $seq = $self->sequence(); | |
| 118 $seq =~ s/(.{80})/$1\n/g; | |
| 119 push( @output, $seq ); | |
| 120 return join( "\n", @output ); | |
| 121 } | |
| 122 | |
| 123 sub escape { | |
| 124 my ($self, $str) = @_; | |
| 125 $str =~ s/,/%2C/g; | |
| 126 $str =~ s/=/%3D/g; | |
| 127 $str =~ s/;/%3B/g; | |
| 128 $str =~ s/\t/%09/g; | |
| 129 return $str; | |
| 130 } | |
| 131 | |
| 132 sub FT_SO_map { | |
| 133 my ( $self, $key ) = @_; | |
| 134 if ( $key_mapping{$key} ) { | |
| 135 my @result = @{ $key_mapping{$key} }; | |
| 136 return $result[0]; | |
| 137 } | |
| 138 else { | |
| 139 return 'region'; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 sub source_map { | |
| 144 my ( $self, $type ) = @_; | |
| 145 if ( $self->{'override_source'} ) { | |
| 146 return $self->{'override_source'}; | |
| 147 } | |
| 148 if ( $self->{'annotation_software'}{$type} ) { | |
| 149 return $self->{'annotation_software'}{$type}; | |
| 150 } | |
| 151 else { | |
| 152 return '.'; | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 sub get_attrs { | |
| 157 my ( $self, %data ) = @_; | |
| 158 my $feature = $data{'feat'}; | |
| 159 my $parents_ref = $data{'parents'}; | |
| 160 | |
| 161 my %attrs = (); | |
| 162 $attrs{'ID'} = | |
| 163 $self->id_prefix() . '.' . ($self->global_feat_idx()); | |
| 164 | |
| 165 $self->global_feat_idx($self->global_feat_idx()+1); | |
| 166 | |
| 167 # Handle Identifier | |
| 168 my $identifier = $bio->_getIdentifier($feature); | |
| 169 if ( $identifier ne 'ERROR' ) { | |
| 170 $attrs{'Name'} = $identifier . '.' . $feature->primary_tag; | |
| 171 } | |
| 172 | |
| 173 # Handle parents, if there are any | |
| 174 if ($parents_ref) { | |
| 175 if ( ref($parents_ref) eq 'ARRAY' ) { | |
| 176 $attrs{'Parent'} = $parents_ref; | |
| 177 } | |
| 178 else { | |
| 179 $attrs{'Parent'} = [$parents_ref]; | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 # These are otherwise "Special" keys that need to be handled differently. | |
| 184 if ( $feature->has_tag('note') ) { | |
| 185 my @notes = $feature->get_tag_values('note'); | |
| 186 $attrs{'Note'} = \@notes; | |
| 187 } | |
| 188 if ( $feature->has_tag('db_xref') ) { | |
| 189 my @dbxref = $feature->get_tag_values('db_xref'); | |
| 190 $attrs{'Dbxref'} = \@dbxref; | |
| 191 } | |
| 192 | |
| 193 # Do the rest | |
| 194 for my $tag ( $feature->get_all_tags() ) { | |
| 195 | |
| 196 # If not one of the specially handled ones | |
| 197 if ( $tag ne 'name' && $tag ne 'note' && $tag ne 'db_xref' ) { | |
| 198 | |
| 199 # If not a reserved_key | |
| 200 if ( !$reserved_keys{$tag} ) { | |
| 201 my @vals = $feature->get_tag_values($tag); | |
| 202 $attrs{lc($tag)} = \@vals; | |
| 203 } | |
| 204 else { | |
| 205 warn | |
| 206 "Trying to set a reserved key $tag with value $attrs{$tag}"; | |
| 207 } | |
| 208 } | |
| 209 } | |
| 210 my %response = ( | |
| 211 id => $attrs{'ID'}, | |
| 212 attr_str => $self->post_process_attribute_string(%attrs), | |
| 213 ); | |
| 214 return %response; | |
| 215 } | |
| 216 | |
| 217 sub post_process_attribute_string { | |
| 218 my ($self,%attrs) = @_; | |
| 219 my @parts = (); | |
| 220 for my $k ( keys %attrs ) { | |
| 221 | |
| 222 # IGNORED TAGS | |
| 223 if ( $k ne 'translation' && $k ne 'product' ) { | |
| 224 my $joined = | |
| 225 $self->escape_and_join_attribute_subpart( $attrs{$k} ); | |
| 226 push @parts, "$k=$joined"; | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 #print STDERR join("\t",@parts),"\n"; | |
| 231 return join( ";", @parts ); | |
| 232 | |
| 233 } | |
| 234 | |
| 235 sub escape_and_join_attribute_subpart { | |
| 236 my ($self,$ref) = @_; | |
| 237 if ( ref($ref) eq 'ARRAY' ) { | |
| 238 my @attrs = @{$ref}; | |
| 239 return join( ",", map { $self->escape($_) } @attrs ); | |
| 240 } | |
| 241 else { #scalar | |
| 242 return $self->escape($ref); | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 sub get_source { | |
| 247 my ($self) = @_; | |
| 248 if ( !$self->source() ) { | |
| 249 $self->auto_source(); | |
| 250 } | |
| 251 return $self->source(); | |
| 252 } | |
| 253 | |
| 254 sub auto_source { | |
| 255 | |
| 256 # Auto generate a source feature, in case there isn't one. | |
| 257 my ($self) = @_; | |
| 258 my @region = ( | |
| 259 $self->seqid(), | |
| 260 ( $self->genbank() ? 'Genbank' : 'Assembly' ), | |
| 261 'contig', | |
| 262 1, | |
| 263 $self->get_length, | |
| 264 '.', | |
| 265 '.', | |
| 266 '.', | |
| 267 sprintf( "ID=%s;Name=%s", $self->seqid(), $self->seqid() ) | |
| 268 . ( $self->is_circular() ? ";Is_circular=True" : "" ) | |
| 269 ); | |
| 270 $self->source(\@region); | |
| 271 } | |
| 272 | |
| 273 sub add_feature { | |
| 274 my ( $self, $feat ) = @_; | |
| 275 my $primary_tag = $feat->primary_tag; | |
| 276 if ( $primary_tag eq 'CDS' ) { | |
| 277 my ( $id, $data_0 ) = $self->_add_gene( feat => $feat, ); | |
| 278 my ($data_1) = $self->_add_feature( | |
| 279 feat => $feat, | |
| 280 parent => $id, | |
| 281 ); | |
| 282 $self->_low_level_add_feature($data_0); | |
| 283 $self->_low_level_add_feature($data_1); | |
| 284 } | |
| 285 elsif ( $primary_tag eq 'source' ) { | |
| 286 my ($data) = $self->_add_feature( feat => $feat, ); | |
| 287 | |
| 288 # YUCK. | |
| 289 my @z = @{$data}; | |
| 290 my $seqid = $self->seqid(); | |
| 291 $z[8] =~ s/ID=[^;]*;/ID=$seqid;/g; | |
| 292 $self->source( \@z ); | |
| 293 } | |
| 294 elsif ( $primary_tag ne 'gene' ) { | |
| 295 my ($data) = $self->_add_feature( feat => $feat, ); | |
| 296 $self->_low_level_add_feature($data); | |
| 297 } | |
| 298 | |
| 299 } | |
| 300 | |
| 301 sub _low_level_add_feature { | |
| 302 my ( $self, $data ) = @_; | |
| 303 push( @{ $self->data() }, $data ); | |
| 304 } | |
| 305 | |
| 306 sub _add_feature { | |
| 307 my ( $self, %data ) = @_; | |
| 308 my %attrs = | |
| 309 $self->get_attrs( feat => $data{feat}, parents => $data{parent} ); | |
| 310 my @data = ( | |
| 311 $self->seqid(), | |
| 312 $self->source_map( $data{feat}->primary_tag ), | |
| 313 $self->FT_SO_map( $data{feat}->primary_tag ), | |
| 314 $data{feat}->start, | |
| 315 $data{feat}->end, | |
| 316 '.', | |
| 317 ( $data{feat}->strand == 1 ? '+' : '-' ), | |
| 318 '.', | |
| 319 $attrs{attr_str} | |
| 320 ); | |
| 321 | |
| 322 #$self->_low_level_add_feature(\@data); | |
| 323 return \@data; | |
| 324 } | |
| 325 | |
| 326 sub _add_gene { | |
| 327 my ( $self, %data ) = @_; | |
| 328 my $id = $self->id_prefix() . '.' . ( $self->global_feat_idx()); | |
| 329 $self->global_feat_idx($self->global_feat_idx()+1); | |
| 330 my $gene_count = ++$feat_type_count{'gene'}; | |
| 331 my @data = ( | |
| 332 $self->seqid(), | |
| 333 $self->source_map( $data{feat}->primary_tag ), | |
| 334 $self->FT_SO_map('gene'), | |
| 335 $data{feat}->start, | |
| 336 $data{feat}->end, | |
| 337 '.', | |
| 338 ( $data{feat}->strand == 1 ? '+' : '-' ), | |
| 339 '.', | |
| 340 "ID=$id;Name=Gene$gene_count" | |
| 341 ); | |
| 342 | |
| 343 #$self->_low_level_add_feature(\@data); | |
| 344 return ( $id, \@data ); | |
| 345 } | |
| 346 | |
| 347 no Moose; | |
| 348 1; | |
| 349 | |
| 350 __END__ | |
| 351 | |
| 352 =pod | |
| 353 | |
| 354 =encoding UTF-8 | |
| 355 | |
| 356 =head1 NAME | |
| 357 | |
| 358 CPT::GBK2GFF3 | |
| 359 | |
| 360 =head1 VERSION | |
| 361 | |
| 362 version 1.99.4 | |
| 363 | |
| 364 =head1 AUTHOR | |
| 365 | |
| 366 Eric Rasche <rasche.eric@yandex.ru> | |
| 367 | |
| 368 =head1 COPYRIGHT AND LICENSE | |
| 369 | |
| 370 This software is Copyright (c) 2014 by Eric Rasche. | |
| 371 | |
| 372 This is free software, licensed under: | |
| 373 | |
| 374 The GNU General Public License, Version 3, June 2007 | |
| 375 | |
| 376 =cut |
