File Coverage

blib/lib/Bio/Palantir/Parser/Root.pm
Criterion Covered Total %
statement 93 130 71.5
branch 12 28 42.8
condition 6 6 100.0
subroutine 10 11 90.9
pod 0 1 0.0
total 121 176 68.7


line stmt bran cond sub pod time code
1             package Bio::Palantir::Parser::Root;
2             # ABSTRACT: BiosynML DTD-derived internal class
3             $Bio::Palantir::Parser::Root::VERSION = '0.211420';
4 1     1   865 use Moose;
  1         3  
  1         10  
5 1     1   8785 use namespace::autoclean;
  1         2  
  1         14  
6              
7             # AUTOGENERATED CODE! DO NOT MODIFY THIS FILE!
8              
9 1     1   102 use XML::Bare qw(forcearray);
  1         2  
  1         87  
10 1     1   11 use POSIX;
  1         2  
  1         17  
11              
12 1     1   2378 use aliased 'Bio::Palantir::Parser::Cluster';
  1         3  
  1         7  
13 1     1   114 use aliased 'Bio::Palantir::Parser::Gene';
  1         2  
  1         7  
14 1     1   173 use aliased 'Bio::Palantir::Parser::Domain';
  1         3  
  1         4  
15 1     1   181 use aliased 'Bio::Palantir::Parser::Motif';
  1         2  
  1         4  
16              
17 1     1   160 use aliased 'Bio::Palantir::Roles::Modulable::Module';
  1         3  
  1         5  
18              
19             # private attributes
20             has '_root' => (
21             is => 'ro',
22             isa => 'HashRef',
23             required => 1,
24             );
25              
26             has 'module_delineation' => (
27             is => 'ro',
28             isa => 'Str',
29             );
30              
31              
32              
33             # public array(s) of composed objects
34              
35              
36             has 'clusters' => (
37             traits => ['Array'],
38             is => 'ro',
39             isa => 'ArrayRef[Bio::Palantir::Parser::Cluster]',
40             writer => '_set_clusters',
41             init_arg => undef,
42             handles => {
43             count_clusters => 'count',
44             all_clusters => 'elements',
45             get_cluster => 'get',
46             next_cluster => 'shift',
47             },
48             );
49              
50              
51             ## no critic (ProhibitUnusedPrivateSubroutines)
52              
53              
54             ## use critic
55              
56              
57              
58             has 'genes' => (
59             traits => ['Array'],
60             is => 'ro',
61             isa => 'ArrayRef[Bio::Palantir::Parser::Gene]',
62             writer => '_set_genes',
63             handles => {
64             count_genes => 'count',
65             all_genes => 'elements',
66             get_gene => 'get',
67             next_gene => 'shift',
68             },
69             );
70              
71              
72             ## no critic (ProhibitUnusedPrivateSubroutines)
73              
74              
75             ## use critic
76              
77              
78              
79             has 'domains' => (
80             traits => ['Array'],
81             is => 'ro',
82             isa => 'ArrayRef[Bio::Palantir::Parser::Domain]',
83             writer => '_set_domains',
84             handles => {
85             count_domains => 'count',
86             all_domains => 'elements',
87             get_domain => 'get',
88             next_domain => 'shift',
89             },
90             );
91              
92              
93             ## no critic (ProhibitUnusedPrivateSubroutines)
94              
95              
96             ## use critic
97              
98              
99              
100             has 'motifs' => (
101             traits => ['Array'],
102             is => 'ro',
103             isa => 'ArrayRef[Bio::Palantir::Parser::Motif]',
104             writer => '_set_motifs',
105             handles => {
106             count_motifs => 'count',
107             all_motifs => 'elements',
108             get_motif => 'get',
109             next_motif => 'shift',
110             },
111             );
112              
113              
114             ## no critic (ProhibitUnusedPrivateSubroutines)
115              
116              
117             ## use critic
118              
119              
120              
121             # public composed object(s)
122              
123              
124             # public deep methods
125              
126              
127             # public methods
128              
129              
130              
131             # public aliases
132             sub BUILD {
133 1     1 0 9 my $self = shift;
134              
135 21         694 my @motifs = map { Motif->new( _root => $_ ) } @{
136 1         5 forcearray $self->_root->{'motiflist'}{'motif'}
  1         40  
137             };
138              
139 1         48 $self->_set_motifs( \@motifs );
140              
141 1         2 my %motifs_in;
142 1         6 push @{ $motifs_in{ $_->_root->{'domainID'}->{'value'} } }, $_ for @motifs;
  21         622  
143              
144 1         3 my @domains;
145 1         3 for my $domain ( @{ forcearray $self->_root->{'domainlist'}{'domain'} }) {
  1         33  
146             push @domains, Domain->new(
147             _root => $domain,
148 15   100     599 motifs => $motifs_in{ $domain->{id}{value} } // [],
149             );
150             }
151              
152 1         45 $self->_set_domains( \@domains );
153            
154 1         2 my %domains_in;
155             push @{
156 15         430 $domains_in{ $_->_root->{'location'}->{'gene'}->{'geneid'}->{'value'} }
157 1         3 }, $_ for @domains
158             ;
159              
160 1         3 my %orphan_motifs_in;
161             push @{
162 2         62 $orphan_motifs_in{ $_->_root->{'geneID'}->{'value'} }
163 1         3 }, $_ for @{ $motifs_in{'0'} }
  1         4  
164             ;
165            
166 1         4 my @genes;
167 1         3 for my $gene ( @{ forcearray $self->_root->{'genelist'}->{'gene'} }) {
  1         33  
168             my $gene_object = Gene->new(
169             _root => $gene,
170             domains => $domains_in{ $gene->{id}{value} } // [],
171 79   100     3312 orphan_motifs => $orphan_motifs_in{ $gene->{id}{value} } // [],
      100        
172             );
173            
174             # fill monomer attribute
175 79         301 my @monomers = $gene_object->monomers;
176 79 100       184 if (@monomers) {
177              
178 3         137 for my $domain ($gene_object->all_domains) {
179              
180 10 100       38 if ($domain->function =~ m/^A$ | ^A-OX$ | ^AT/xms) {
181 3         109 $domain->_set_monomer( shift @monomers );
182             }
183             }
184             }
185              
186 79         257 push @genes, $gene_object;
187             }
188              
189 1         61 $self->_set_genes( \@genes );
190            
191 1         4 my @modules;
192 1 50       38 if ($self->_root->{modulelist}) {
193 0         0 @modules = _extract_antismash_modules($self->_root, @genes);
194             }
195            
196 1         4 my $cluster_rank = 1;
197            
198             # fix for antismash5: cluster order is not preserved during json2xml conversion
199 1         3 my $cluster_list;
200 1 50       3 unless (
201 6         46 grep { $_->{genecluster}->{sequence}->{value} }
202 1         28 @{ forcearray $self->_root->{'model'} })
203             {
204             # filter clusters by coordinates order for antiSMASH 5 (and 3)
205             $cluster_list
206             = [ sort { $a->{genecluster}->{region}->{begin}->{value}
207 0         0 <=> $b->{genecluster}->{region}->{begin}->{value} }
208 0         0 @{ forcearray $self->_root->{'model'} } ]
  0         0  
209             ;
210             }
211              
212             else {
213             # do not filter clusters as antismash 4 reboots coordinates per contig
214 1         4 $cluster_list = [ @{ forcearray $self->_root->{'model'} } ];
  1         38  
215             }
216            
217 1         9 my @clusters;
218 1         4 for my $cluster (@$cluster_list) {
219 6         34 my $begin = $cluster->{'genecluster'}->{'region'}->{'begin'}->{'value'};
220 6         14 my $end = $cluster->{'genecluster'}->{'region'}->{'end' }->{'value'};
221              
222             # fix for antiSMASH 4: keep coordinates in strand + order
223 6 50       30 my $strand = $begin < $end ? '+' : '-';
224            
225 6 50       23 if ($strand eq '-') {
226 0         0 my $temp_begin = $begin;
227              
228 0         0 $begin = $end;
229 0         0 $end = $temp_begin;
230             }
231              
232             # second fix for antiSMASH 4: handle the coordinates reset for each contig (use of the sequence value which is only exploited in antiSMASH 4)
233 6         11 my @cluster_genes;
234 6 50       21 if ($cluster->{'genecluster'}->{'sequence'}->{'value'}) {
235             my $cluster_seqlist
236 6         17 = $cluster->{'genecluster'}->{'sequence'}->{'value'};
237              
238             # filter on the sequence value and the cluster coordinates
239 474 100       1254 @cluster_genes = grep { $_->genomic_dna_begin < $end
240             && $_->genomic_dna_end > $begin }
241 6         18 grep { $_->_root->{'sequence'}{'value'}
  474         12754  
242             eq $cluster_seqlist } @genes
243             ;
244             }
245            
246             # this information is not extracted in antiSMASH 5 (but coordinates are continuous)
247             else {
248 0 0       0 @cluster_genes = grep { $_->genomic_dna_begin < $end
  0         0  
249             && $_->genomic_dna_end > $begin } @genes;
250             }
251            
252             @cluster_genes
253 6         46 = sort { $a->genomic_dna_begin <=> $b->genomic_dna_begin }
  167         449  
254             @cluster_genes
255             ;
256              
257 6         14 my $gene_rank = 1;
258 6         14 my $domain_rank = 1;
259              
260 6         19 for my $gene (@cluster_genes) {
261              
262             $_->_set_rank($domain_rank++)
263 79         2813 for sort { $a->begin <=> $b->begin } $gene->all_domains;
  12         63  
264              
265 79         2449 $gene->_set_rank($gene_rank++);
266             }
267              
268             # fix antiSMASH 5.1 and its module delineation
269 6 50       186 if ($self->_root->{modulelist}) { # Add new module feature from version 5.1
270              
271             # TODO see how synchronize domain rank in @genes and @modules
272             my @cluster_modules =
273 0         0 sort {$a->genomic_prot_begin <=> $b->genomic_prot_begin }
274 0 0       0 grep { $_->genomic_dna_begin < $end
  0         0  
275             && $_->genomic_dna_end > $begin }
276             @modules;
277             ;
278            
279 0         0 my $mrank = 1;
280 0         0 $_->_set_rank($mrank++) for @cluster_modules;
281            
282             push @clusters, Cluster->new(
283             module_delineation => $self->module_delineation,
284             rank => $cluster_rank,
285 0         0 _root => $cluster->{'genecluster'},
286             genes => \@cluster_genes,
287             genomic_dna_begin => $begin,
288             genomic_dna_end => $end,
289             genomic_prot_begin => ceil($begin / 3),
290             genomic_prot_end => floor($end / 3),
291             modules => \@cluster_modules,
292             );
293             }
294              
295             else {
296             push @clusters, Cluster->new(
297             module_delineation => $self->module_delineation,
298             rank => $cluster_rank,
299 6         191 _root => $cluster->{'genecluster'},
300             genes => \@cluster_genes,
301             genomic_dna_begin => $begin,
302             genomic_dna_end => $end,
303             genomic_prot_begin => ceil($begin / 3),
304             genomic_prot_end => floor($end / 3),
305             );
306             }
307              
308 6         29 $cluster_rank++;
309             }
310              
311             # enables module cutting mode
312 1         39 $_->_set_cutting_mode( $self->module_delineation ) for @clusters;
313              
314 1         38 $self->_set_clusters( \@clusters );
315              
316 1         49 return;
317             }
318              
319             sub _extract_antismash_modules {
320              
321 0     0     my ($root, @report_genes) = @_;
322              
323 0           my @report_modules;
324 0           for my $module ( @{ forcearray $root->{'modulelist'}{'module'} }) {
  0            
325              
326             next
327 0 0         if $module->{complete}{value} eq 'false';
328              
329             my @mgenes
330             = grep { $module->{prot_start}{value} < $_->genomic_prot_end
331 0 0         && $module->{prot_end}{value} > $_->genomic_prot_begin }
332 0           sort { $a->genomic_prot_begin <=> $b->genomic_prot_end }
  0            
333             @report_genes
334             ;
335              
336 0           my $gene_ids = [map { $_->uui } @mgenes]; # ArrayRef Module attribute
  0            
337              
338 0           my $mdomains;
339 0           for my $gene (@mgenes) {
340              
341 0           push @{ $mdomains },
342 0           sort { $a->begin <=> $b->begin }
343 0           grep { ($_->begin + $gene->genomic_prot_begin - 1) # -1 for beginning domain pos 1 in gene pos 1
344             < $module->{prot_end}{value}
345             && ($_->end + $gene->genomic_prot_begin - 1)
346 0 0         > $module->{prot_start}{value} }
347             $gene->all_domains
348             ;
349             }
350              
351 0           my $module_sequence = join '', map { $_->protein_sequence }
352 0           @{ $mdomains };
  0            
353             # my $module_sequence = substr(
354             # $module->{prot_begin} - 1,
355             # $module->{prot_end} - $module->{prot_begin} + 1,
356             # $mgene->protein_sequence
357             # );
358             #
359            
360             my $size
361 0           = $module->{prot_end}{value} - $module->{prot_start}{value} + 1;
362              
363             push @report_modules, Module->new(
364             rank => $module->{id}{value},
365             genomic_prot_begin => $module->{prot_start}{value},
366             genomic_prot_end => $module->{prot_end}{value},
367             genomic_prot_coordinates => [
368             $module->{prot_start}{value},
369             $module->{prot_end}{value},
370 0           ],
371             protein_sequence => $module_sequence,
372             gene_uuis => $gene_ids,
373             domains => $mdomains,
374             size => $size,
375             );
376             }
377              
378 0           return(@report_modules);
379             }
380              
381              
382             __PACKAGE__->meta->make_immutable;
383             1;
384              
385             __END__
386              
387             =pod
388              
389             =head1 NAME
390              
391             Bio::Palantir::Parser::Root - BiosynML DTD-derived internal class
392              
393             =head1 VERSION
394              
395             version 0.211420
396              
397             =head1 SYNOPSIS
398              
399             # TODO
400              
401             =head1 DESCRIPTION
402              
403             # TODO
404              
405             =head1 ATTRIBUTES
406              
407             =head2 clusters
408              
409             ArrayRef of L<Bio::Palantir::Parser::Cluster>
410              
411             =head2 genes
412              
413             ArrayRef of L<Bio::Palantir::Parser::Gene>
414              
415             =head2 domains
416              
417             ArrayRef of L<Bio::Palantir::Parser::Domain>
418              
419             =head2 motifs
420              
421             ArrayRef of L<Bio::Palantir::Parser::Motif>
422              
423             =head1 METHODS
424              
425             =head2 count_clusters
426              
427             Returns the number of Clusters of the Root.
428              
429             # $root is a Bio::Palantir::Parser::Root
430             my $count = $root->count_clusters;
431              
432             This method does not accept any arguments.
433              
434             =head2 all_clusters
435              
436             Returns all the Clusters of the Root (not an array reference).
437              
438             # $root is a Bio::Palantir::Parser::Root
439             my @clusters = $root->all_clusters;
440              
441             This method does not accept any arguments.
442              
443             =head2 get_cluster
444              
445             Returns one Cluster of the Root by its index. You can also use
446             negative index numbers, just as with Perl's core array handling. If the
447             specified Cluster does not exist, this method will return C<undef>.
448              
449             # $root is a Bio::Palantir::Parser::Root
450             my $cluster = $root->get_cluster($index);
451             croak "Cluster $index not found!" unless defined $cluster;
452              
453             This method accepts just one argument (and not an array slice).
454              
455             =head2 next_cluster
456              
457             Shifts the first Cluster of the array off and returns it, shortening the
458             array by 1 and moving everything down. If there are no more Clusters in
459             the array, returns C<undef>.
460              
461             # $root is a Bio::Palantir::Parser::Root
462             while (my $cluster = $root->next_cluster) {
463             # process $cluster
464             # ...
465             }
466              
467             This method does not accept any arguments.
468              
469             =head2 count_genes
470              
471             Returns the number of Genes of the Root.
472              
473             # $root is a Bio::Palantir::Parser::Root
474             my $count = $root->count_genes;
475              
476             This method does not accept any arguments.
477              
478             =head2 all_genes
479              
480             Returns all the Genes of the Root (not an array reference).
481              
482             # $root is a Bio::Palantir::Parser::Root
483             my @genes = $root->all_genes;
484              
485             This method does not accept any arguments.
486              
487             =head2 get_gene
488              
489             Returns one Gene of the Root by its index. You can also use
490             negative index numbers, just as with Perl's core array handling. If the
491             specified Gene does not exist, this method will return C<undef>.
492              
493             # $root is a Bio::Palantir::Parser::Root
494             my $gene = $root->get_gene($index);
495             croak "Gene $index not found!" unless defined $gene;
496              
497             This method accepts just one argument (and not an array slice).
498              
499             =head2 next_gene
500              
501             Shifts the first Gene of the array off and returns it, shortening the
502             array by 1 and moving everything down. If there are no more Genes in
503             the array, returns C<undef>.
504              
505             # $root is a Bio::Palantir::Parser::Root
506             while (my $gene = $root->next_gene) {
507             # process $gene
508             # ...
509             }
510              
511             This method does not accept any arguments.
512              
513             =head2 count_domains
514              
515             Returns the number of Domains of the Root.
516              
517             # $root is a Bio::Palantir::Parser::Root
518             my $count = $root->count_domains;
519              
520             This method does not accept any arguments.
521              
522             =head2 all_domains
523              
524             Returns all the Domains of the Root (not an array reference).
525              
526             # $root is a Bio::Palantir::Parser::Root
527             my @domains = $root->all_domains;
528              
529             This method does not accept any arguments.
530              
531             =head2 get_domain
532              
533             Returns one Domain of the Root by its index. You can also use
534             negative index numbers, just as with Perl's core array handling. If the
535             specified Domain does not exist, this method will return C<undef>.
536              
537             # $root is a Bio::Palantir::Parser::Root
538             my $domain = $root->get_domain($index);
539             croak "Domain $index not found!" unless defined $domain;
540              
541             This method accepts just one argument (and not an array slice).
542              
543             =head2 next_domain
544              
545             Shifts the first Domain of the array off and returns it, shortening the
546             array by 1 and moving everything down. If there are no more Domains in
547             the array, returns C<undef>.
548              
549             # $root is a Bio::Palantir::Parser::Root
550             while (my $domain = $root->next_domain) {
551             # process $domain
552             # ...
553             }
554              
555             This method does not accept any arguments.
556              
557             =head2 count_motifs
558              
559             Returns the number of Motifs of the Root.
560              
561             # $root is a Bio::Palantir::Parser::Root
562             my $count = $root->count_motifs;
563              
564             This method does not accept any arguments.
565              
566             =head2 all_motifs
567              
568             Returns all the Motifs of the Root (not an array reference).
569              
570             # $root is a Bio::Palantir::Parser::Root
571             my @motifs = $root->all_motifs;
572              
573             This method does not accept any arguments.
574              
575             =head2 get_motif
576              
577             Returns one Motif of the Root by its index. You can also use
578             negative index numbers, just as with Perl's core array handling. If the
579             specified Motif does not exist, this method will return C<undef>.
580              
581             # $root is a Bio::Palantir::Parser::Root
582             my $motif = $root->get_motif($index);
583             croak "Motif $index not found!" unless defined $motif;
584              
585             This method accepts just one argument (and not an array slice).
586              
587             =head2 next_motif
588              
589             Shifts the first Motif of the array off and returns it, shortening the
590             array by 1 and moving everything down. If there are no more Motifs in
591             the array, returns C<undef>.
592              
593             # $root is a Bio::Palantir::Parser::Root
594             while (my $motif = $root->next_motif) {
595             # process $motif
596             # ...
597             }
598              
599             This method does not accept any arguments.
600              
601             =head1 AUTHOR
602              
603             Loic MEUNIER <lmeunier@uliege.be>
604              
605             =head1 COPYRIGHT AND LICENSE
606              
607             This software is copyright (c) 2019 by University of Liege / Unit of Eukaryotic Phylogenomics / Loic MEUNIER and Denis BAURAIN.
608              
609             This is free software; you can redistribute it and/or modify it under
610             the same terms as the Perl 5 programming language system itself.
611              
612             =cut