File Coverage

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


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.200700';
4 1     1   552 use Moose;
  1         3  
  1         5  
5 1     1   5836 use namespace::autoclean;
  1         2  
  1         8  
6              
7             # AUTOGENERATED CODE! DO NOT MODIFY THIS FILE!
8              
9 1     1   82 use XML::Bare qw(forcearray);
  1         2  
  1         61  
10 1     1   6 use POSIX;
  1         1  
  1         4  
11              
12 1     1   1593 use aliased 'Bio::Palantir::Parser::Cluster';
  1         3  
  1         7  
13 1     1   112 use aliased 'Bio::Palantir::Parser::Gene';
  1         3  
  1         6  
14 1     1   145 use aliased 'Bio::Palantir::Parser::Domain';
  1         2  
  1         4  
15 1     1   128 use aliased 'Bio::Palantir::Parser::Motif';
  1         2  
  1         3  
16              
17 1     1   127 use aliased 'Bio::Palantir::Roles::Modulable::Module';
  1         2  
  1         4  
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 3 my $self = shift;
134              
135 21         581 my @motifs = map { Motif->new( _root => $_ ) } @{
136 1         2 forcearray $self->_root->{'motiflist'}{'motif'}
  1         39  
137             };
138              
139 1         43 $self->_set_motifs( \@motifs );
140              
141 1         2 my %motifs_in;
142 1         5 push @{ $motifs_in{ $_->_root->{'domainID'}->{'value'} } }, $_ for @motifs;
  21         514  
143              
144 1         3 my @domains;
145 1         2 for my $domain ( @{ forcearray $self->_root->{'domainlist'}{'domain'} }) {
  1         28  
146             push @domains, Domain->new(
147             _root => $domain,
148 15   100     527 motifs => $motifs_in{ $domain->{id}{value} } // [],
149             );
150             }
151              
152 1         39 $self->_set_domains( \@domains );
153            
154 1         2 my %domains_in;
155             push @{
156 15         376 $domains_in{ $_->_root->{'location'}->{'gene'}->{'geneid'}->{'value'} }
157 1         4 }, $_ for @domains
158             ;
159              
160 1         3 my %orphan_motifs_in;
161             push @{
162 2         50 $orphan_motifs_in{ $_->_root->{'geneID'}->{'value'} }
163 1         3 }, $_ for @{ $motifs_in{'0'} }
  1         7  
164             ;
165            
166 1         4 my @genes;
167 1         2 for my $gene ( @{ forcearray $self->_root->{'genelist'}->{'gene'} }) {
  1         27  
168             my $gene_object = Gene->new(
169             _root => $gene,
170             domains => $domains_in{ $gene->{id}{value} } // [],
171 79   100     2569 orphan_motifs => $orphan_motifs_in{ $gene->{id}{value} } // [],
      100        
172             );
173            
174             # fill monomer attribute
175 79         223 my @monomers = $gene_object->monomers;
176 79 100       166 if (@monomers) {
177              
178 3         101 for my $domain ($gene_object->all_domains) {
179              
180 10 100       31 if ($domain->function =~ m/^A$ | ^A-OX$ | ^AT/xms) {
181 3         99 $domain->_set_monomer( shift @monomers );
182             }
183             }
184             }
185              
186 79         185 push @genes, $gene_object;
187             }
188              
189 1         39 $self->_set_genes( \@genes );
190            
191 1         1 my @modules;
192 1 50       33 if ($self->_root->{modulelist}) {
193 0         0 @modules = _extract_antismash_modules($self->_root, @genes);
194             }
195            
196 1         2 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         33 grep { $_->{genecluster}->{sequence}->{value} }
202 1         24 @{ 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         2 $cluster_list = [ @{ forcearray $self->_root->{'model'} } ];
  1         23  
215             }
216            
217 1         8 my @clusters;
218 1         4 for my $cluster (@$cluster_list) {
219 6         33 my $begin = $cluster->{'genecluster'}->{'region'}->{'begin'}->{'value'};
220 6         18 my $end = $cluster->{'genecluster'}->{'region'}->{'end' }->{'value'};
221              
222             # fix for antiSMASH 4: keep coordinates in strand + order
223 6 50       44 my $strand = $begin < $end ? '+' : '-';
224            
225 6 50       25 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         9 my @cluster_genes;
234 6 50       58 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       1076 @cluster_genes = grep { $_->genomic_dna_begin < $end
240             && $_->genomic_dna_end > $begin }
241 6         22 grep { $_->_root->{'sequence'}{'value'}
  474         10274  
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         49 = sort { $a->genomic_dna_begin <=> $b->genomic_dna_begin }
  167         356  
254             @cluster_genes
255             ;
256              
257 6         13 my $gene_rank = 1;
258 6         16 my $domain_rank = 1;
259              
260 6         25 for my $gene (@cluster_genes) {
261              
262             $_->_set_rank($domain_rank++)
263 79         2237 for sort { $a->begin <=> $b->begin } $gene->all_domains;
  12         53  
264              
265 79         1950 $gene->_set_rank($gene_rank++);
266             }
267              
268             # fix antiSMASH 5.1 and its module delineation
269 6 50       162 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         184 _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         24 $cluster_rank++;
309             }
310              
311             # enables module cutting mode
312 1         35 $_->_set_cutting_mode( $self->module_delineation ) for @clusters;
313              
314 1         50 $self->_set_clusters( \@clusters );
315              
316 1         37 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             push @report_modules, Module->new(
360             rank => $module->{id}{value},
361             genomic_prot_begin => $module->{prot_start}{value},
362             genomic_prot_end => $module->{prot_end}{value},
363             genomic_prot_coordinates => [
364             $module->{prot_start}{value},
365             $module->{prot_end}{value},
366 0           ],
367             protein_sequence => $module_sequence,
368             gene_uuis => $gene_ids,
369             domains => $mdomains,
370             );
371             }
372              
373 0           return(@report_modules);
374             }
375              
376              
377             __PACKAGE__->meta->make_immutable;
378             1;
379              
380             __END__
381              
382             =pod
383              
384             =head1 NAME
385              
386             Bio::Palantir::Parser::Root - BiosynML DTD-derived internal class
387              
388             =head1 VERSION
389              
390             version 0.200700
391              
392             =head1 SYNOPSIS
393              
394             # TODO
395              
396             =head1 DESCRIPTION
397              
398             # TODO
399              
400             =head1 ATTRIBUTES
401              
402             =head2 clusters
403              
404             ArrayRef of L<Bio::Palantir::Parser::Cluster>
405              
406             =head2 genes
407              
408             ArrayRef of L<Bio::Palantir::Parser::Gene>
409              
410             =head2 domains
411              
412             ArrayRef of L<Bio::Palantir::Parser::Domain>
413              
414             =head2 motifs
415              
416             ArrayRef of L<Bio::Palantir::Parser::Motif>
417              
418             =head1 METHODS
419              
420             =head2 count_clusters
421              
422             Returns the number of Clusters of the Root.
423              
424             # $root is a Bio::Palantir::Parser::Root
425             my $count = $root->count_clusters;
426              
427             This method does not accept any arguments.
428              
429             =head2 all_clusters
430              
431             Returns all the Clusters of the Root (not an array reference).
432              
433             # $root is a Bio::Palantir::Parser::Root
434             my @clusters = $root->all_clusters;
435              
436             This method does not accept any arguments.
437              
438             =head2 get_cluster
439              
440             Returns one Cluster of the Root by its index. You can also use
441             negative index numbers, just as with Perl's core array handling. If the
442             specified Cluster does not exist, this method will return C<undef>.
443              
444             # $root is a Bio::Palantir::Parser::Root
445             my $cluster = $root->get_cluster($index);
446             croak "Cluster $index not found!" unless defined $cluster;
447              
448             This method accepts just one argument (and not an array slice).
449              
450             =head2 next_cluster
451              
452             Shifts the first Cluster of the array off and returns it, shortening the
453             array by 1 and moving everything down. If there are no more Clusters in
454             the array, returns C<undef>.
455              
456             # $root is a Bio::Palantir::Parser::Root
457             while (my $cluster = $root->next_cluster) {
458             # process $cluster
459             # ...
460             }
461              
462             This method does not accept any arguments.
463              
464             =head2 count_genes
465              
466             Returns the number of Genes of the Root.
467              
468             # $root is a Bio::Palantir::Parser::Root
469             my $count = $root->count_genes;
470              
471             This method does not accept any arguments.
472              
473             =head2 all_genes
474              
475             Returns all the Genes of the Root (not an array reference).
476              
477             # $root is a Bio::Palantir::Parser::Root
478             my @genes = $root->all_genes;
479              
480             This method does not accept any arguments.
481              
482             =head2 get_gene
483              
484             Returns one Gene of the Root by its index. You can also use
485             negative index numbers, just as with Perl's core array handling. If the
486             specified Gene does not exist, this method will return C<undef>.
487              
488             # $root is a Bio::Palantir::Parser::Root
489             my $gene = $root->get_gene($index);
490             croak "Gene $index not found!" unless defined $gene;
491              
492             This method accepts just one argument (and not an array slice).
493              
494             =head2 next_gene
495              
496             Shifts the first Gene of the array off and returns it, shortening the
497             array by 1 and moving everything down. If there are no more Genes in
498             the array, returns C<undef>.
499              
500             # $root is a Bio::Palantir::Parser::Root
501             while (my $gene = $root->next_gene) {
502             # process $gene
503             # ...
504             }
505              
506             This method does not accept any arguments.
507              
508             =head2 count_domains
509              
510             Returns the number of Domains of the Root.
511              
512             # $root is a Bio::Palantir::Parser::Root
513             my $count = $root->count_domains;
514              
515             This method does not accept any arguments.
516              
517             =head2 all_domains
518              
519             Returns all the Domains of the Root (not an array reference).
520              
521             # $root is a Bio::Palantir::Parser::Root
522             my @domains = $root->all_domains;
523              
524             This method does not accept any arguments.
525              
526             =head2 get_domain
527              
528             Returns one Domain of the Root by its index. You can also use
529             negative index numbers, just as with Perl's core array handling. If the
530             specified Domain does not exist, this method will return C<undef>.
531              
532             # $root is a Bio::Palantir::Parser::Root
533             my $domain = $root->get_domain($index);
534             croak "Domain $index not found!" unless defined $domain;
535              
536             This method accepts just one argument (and not an array slice).
537              
538             =head2 next_domain
539              
540             Shifts the first Domain of the array off and returns it, shortening the
541             array by 1 and moving everything down. If there are no more Domains in
542             the array, returns C<undef>.
543              
544             # $root is a Bio::Palantir::Parser::Root
545             while (my $domain = $root->next_domain) {
546             # process $domain
547             # ...
548             }
549              
550             This method does not accept any arguments.
551              
552             =head2 count_motifs
553              
554             Returns the number of Motifs of the Root.
555              
556             # $root is a Bio::Palantir::Parser::Root
557             my $count = $root->count_motifs;
558              
559             This method does not accept any arguments.
560              
561             =head2 all_motifs
562              
563             Returns all the Motifs of the Root (not an array reference).
564              
565             # $root is a Bio::Palantir::Parser::Root
566             my @motifs = $root->all_motifs;
567              
568             This method does not accept any arguments.
569              
570             =head2 get_motif
571              
572             Returns one Motif of the Root by its index. You can also use
573             negative index numbers, just as with Perl's core array handling. If the
574             specified Motif does not exist, this method will return C<undef>.
575              
576             # $root is a Bio::Palantir::Parser::Root
577             my $motif = $root->get_motif($index);
578             croak "Motif $index not found!" unless defined $motif;
579              
580             This method accepts just one argument (and not an array slice).
581              
582             =head2 next_motif
583              
584             Shifts the first Motif of the array off and returns it, shortening the
585             array by 1 and moving everything down. If there are no more Motifs in
586             the array, returns C<undef>.
587              
588             # $root is a Bio::Palantir::Parser::Root
589             while (my $motif = $root->next_motif) {
590             # process $motif
591             # ...
592             }
593              
594             This method does not accept any arguments.
595              
596             =head1 AUTHOR
597              
598             Loic MEUNIER <lmeunier@uliege.be>
599              
600             =head1 COPYRIGHT AND LICENSE
601              
602             This software is copyright (c) 2019 by University of Liege / Unit of Eukaryotic Phylogenomics / Loic MEUNIER and Denis BAURAIN.
603              
604             This is free software; you can redistribute it and/or modify it under
605             the same terms as the Perl 5 programming language system itself.
606              
607             =cut