File Coverage

blib/lib/Bio/Palantir/Roles/Modulable.pm
Criterion Covered Total %
statement 21 129 16.2
branch 0 34 0.0
condition 0 49 0.0
subroutine 7 10 70.0
pod n/a
total 28 222 12.6


line stmt bran cond sub pod time code
1             package Bio::Palantir::Roles::Modulable;
2             # ABSTRACT: Modulable Moose role for Module object construction
3             $Bio::Palantir::Roles::Modulable::VERSION = '0.200700';
4 1     1   639 use Moose::Role;
  1         2  
  1         8  
5              
6 1     1   4618 use autodie;
  1         3  
  1         7  
7              
8 1     1   4686 use Const::Fast;
  1         1024  
  1         5  
9 1     1   63 use feature qw(say);
  1         2  
  1         75  
10              
11 1     1   6 use aliased 'Bio::Palantir::Roles::Modulable::Module';
  1         2  
  1         4  
12 1     1   110 use aliased 'Bio::Palantir::Roles::Modulable::Component';
  1         2  
  1         5  
13              
14             requires 'genes';
15              
16              
17             has 'cutting_mode' => (
18             is => 'ro',
19             isa => 'Str',
20             writer => '_set_cutting_mode',
21             );
22              
23             const my $_modular_domains => qr/^A$ | ^AT | ^C$ | ^CAL$ | ^KS | ^E$ | ^H$
24             | ^PCP$ | ^ACP$ | KR | DH | ER | cyc | ^TE$ | ^Red$
25             | ^NAD | NRPS-COM/xms
26             ;
27              
28             const my %_init_domain_for =>(
29             condensation => qr{^C$ | ^KS$}xms,
30             'substrate-selection' => qr{^A$ | ^AT$}xms,
31             );
32              
33             const my %_term_domain_for =>(
34             condensation => qr/^PCP$ | ^ACP$/xmsi,
35             'substrate-selection' => qr/^C$ | ^KS$/xmsi,
36             );
37              
38             has 'modules' => (
39             traits => ['Array'],
40             is => 'ro',
41             isa => 'ArrayRef[Bio::Palantir::Roles::Modulable::Module]',
42             # init_arg => undef, # due to antiSMASH 5.1's new Module delineation method
43             lazy => 1,
44             default => \&_build_modules, # builder not accepted by perlcritic, likely due to parallel way to fill modules "Private subroutine/method '_build_modules' declared but not used at line 35, column 1. Eliminate dead code. (Severity: 3)"
45             handles => {
46             count_modules => 'count',
47             all_modules => 'elements',
48             get_module => 'get',
49             next_module => 'shift',
50             },
51             );
52              
53             sub _build_modules {
54            
55 0     0     my $self = shift;
56              
57             # process other cluster types
58 0 0         unless ($self->type =~ m/nrps | t1pks/xmsi) {
59 0           return [];
60             }
61              
62 0           my @genes = sort { $a->rank <=> $b->rank } $self->all_genes;
  0            
63              
64 0           my $module_n = 1; #TODO move in Cluster.pm
65 0           my $module_in = 0; # to know if the algo is inside a module or not
66              
67             # avoid redondant condensation or selection domains in a module
68 0           my %nr_domain_for = (
69             condensation => 0,
70             'substrate-selection' => 0,
71             'tailoring/other' => 0,
72             'carrier-protein' => 0,
73             termination => 0,
74             NA => 0,
75             );
76              
77 0           my %module_for;
78 0           my $last_gene = 1;
79              
80             GENE:
81 0           for my $gene_n (0 .. @genes - 1) {
82              
83 0           my @domains = sort { $a->rank <=> $b->rank }
  0            
84             $genes[$gene_n]->all_domains;
85              
86             next GENE
87 0 0         unless @domains;
88              
89             DOMAIN:
90 0           for my $i (0 .. @domains - 1) {
91              
92             # initiate first module based on cutting mode
93 0 0 0       if ( ($domains[$i]->class eq 'condensation'
    0 0        
    0 0        
    0 0        
      0        
94             || $domains[$i]->class eq 'substrate-selection')
95             && ! %module_for) {
96              
97 0           $module_for{$module_n} = {
98             start => $gene_n,
99             end => $gene_n,
100             domains => [$domains[$i]],
101             };
102              
103 0           $last_gene = $gene_n; # for avoiding empty values
104 0           $nr_domain_for{ $domains[$i]->class } = 1;
105 0           $module_in = 1;
106             }
107            
108             # initiate a new module if cutting domain
109             elsif( $domains[$i]->class eq $self->cutting_mode ) {
110              
111 0           $module_for{++$module_n} = {
112             start => $gene_n,
113             end => $gene_n,
114             domains => [$domains[$i]],
115             };
116              
117             # reset non redundant counter
118             $nr_domain_for{$_} = 0
119 0           for qw(condensation substrate-selection);
120              
121 0           $nr_domain_for{ $domains[$i]->class } = 1;
122 0           $module_in = 1;
123             }
124              
125             # terminate a module if:
126             # encounter a non modular domain or trans-acting
127             # OR domains are separated by more than one gene
128             # OR two consecutive selection or condensation domains
129             elsif ( ( ($gene_n - $last_gene) >= 2
130             || ! $domains[$i]->symbol =~ $_modular_domains
131             || $nr_domain_for{ $domains[$i]->class } == 1 )
132             && $module_in == 1) {
133              
134 0           $module_for{$module_n}{end} = $last_gene;
135              
136             $nr_domain_for{$_} = 0
137 0           for qw(condensation 'substrate-selection');
138 0           $module_in = 0;
139             }
140              
141             # module elongation (if inside a module & a modular domain & but not cutting one)
142             elsif( $domains[$i]->symbol =~ $_modular_domains
143             && $module_in == 1) {
144              
145 0 0 0       if ($domains[$i]->class eq 'condensation'
146             || $domains[$i]->class eq 'substrate-selection') {
147 0           $nr_domain_for{ $domains[$i]->class } = 1
148             }
149            
150 0           $module_for{$module_n}{end} = $gene_n;
151              
152 0           push @{ $module_for{$module_n}{domains} }, $domains[$i];
  0            
153             }
154              
155 0           $last_gene = $gene_n; # positionned here to directly update transitions between genes
156             }
157             }
158            
159 0 0         return [] unless %module_for;
160              
161             # define last module end coordinate
162             $module_for{$module_n}{end} = $last_gene
163 0 0         unless $module_for{$module_n}{end};
164              
165             # put C terminal domain in the termination module
166 0 0 0       if ( $module_for{ $module_n }{domains}[0]->symbol
      0        
167             =~ m/^C$ | ^TE$ | ^TD$ | ^Red$ | ^NAD$/xmsi
168 0           && @{ $module_for{ $module_n}{domains} }
169             == 1 && keys %module_for > 1 ) {
170              
171 0           push @{ $module_for{ $module_n - 1 }{domains} },
172 0           @{ $module_for{ $module_n }{domains} };
  0            
173              
174 0           delete $module_for{ $module_n };
175             }
176            
177             # filter modules: considered incomplete if < 2 (min module is starter A-PCP)
178 0           delete $module_for{$_} for grep {
179 0           scalar @{ $module_for{$_}{domains} } < 2 } keys %module_for;
  0            
180              
181 0           my @modules = _create_elmt_array(\%module_for, \@genes, 'module');
182 0           return \@modules;
183             }
184              
185             # Cluster components attribute: ranked modules and trans-acting enzymes
186             has 'components' => (
187             traits => ['Array'],
188             is => 'ro',
189             isa => 'ArrayRef[Bio::Palantir::Roles::Modulable::Component]',
190             init_arg => undef,
191             lazy => 1,
192             default => \&_build_components, # builder not accepted by perlcritic, likely due to parallel way to fill modules "Private subroutine/method '_build_components' declared but not used at line 241, column 1. Eliminate dead code. (Severity: 3)"
193             # builder => '_build_components',
194             handles => {
195             count_components => 'count',
196             all_components => 'elements',
197             get_component => 'get',
198             next_component => 'shift',
199             },
200             );
201              
202             sub _build_components {
203              
204 0     0     my $self = shift;
205              
206 0           my @genes = sort { $a->rank <=> $b->rank } $self->all_genes;
  0            
207              
208 0           my $component_n = 1; #TODO move in Cluster.pm
209 0           my $launched = 0;
210 0           my (%component_for);
211              
212             GENE:
213 0           for my $gene_n (0 .. @genes - 1) {
214              
215 0           my @domains = sort { $a->rank <=> $b->rank }
  0            
216             $genes[$gene_n]->all_domains;
217              
218             next GENE
219 0 0         unless @domains;
220              
221             DOMAIN:
222 0           for my $i (0 .. @domains - 1) {
223              
224             # initiate the components
225 0 0 0       if ($component_n == 1 && $i == 0 && $launched == 0) {
    0 0        
      0        
      0        
      0        
226 0           $component_for{$component_n}{start} = $gene_n;
227 0           $launched = 1;
228             }
229            
230             elsif (
231             # define components anchors on C domains (biological sense with communication domains and condensation steps)
232             ($domains[$i]->symbol
233             =~ $_init_domain_for{ $self->cutting_mode })
234             # do not allow components to be separated by an intermediate gene
235             || ( ($genes[$gene_n]->rank
236             - $genes[ $component_for{$component_n}{start} ]->rank) > 1)
237             # avoid auxilliary domains in components, for instance: if C-A-PCP-TE | AT | A | ACL | ECH assign a different component for last domains
238             || (@domains == 1
239             && $domains[$i]->symbol !~ $_modular_domains)
240             ) {
241 0           $component_for{++$component_n}{start} = $gene_n;
242             }
243            
244 0           push @{ $component_for{$component_n}{domains} }, $domains[$i];
  0            
245 0           $component_for{$component_n}{end} = $gene_n;
246             }
247             }
248              
249 0 0         return [] unless %component_for;
250              
251             # put C terminal domain in the termination component
252 0 0 0       if ($component_for{ $component_n }{domains}[0]->symbol
      0        
253             =~ m/^C$ | ^TE$ | ^TD$ | ^Red$ | ^NAD$/xmsi
254 0           && @{ $component_for{ $component_n}{domains} }
255             == 1
256             && keys %component_for > 1) {
257              
258 0           push @{ $component_for{ $component_n - 1 }{domains} },
259 0           @{ $component_for{ $component_n }{domains} };
  0            
260              
261 0           delete $component_for{ $component_n };
262             }
263              
264 0           my @components = _create_elmt_array(\%component_for, \@genes, 'component');
265 0           return \@components;
266             }
267              
268             sub _create_elmt_array {
269              
270 0     0     my ($element_for, $genes, $str) = @_;
271              
272 0           my (@elements, $rank);
273 0           for my $element_n (sort { $a <=> $b } keys %{ $element_for }) {
  0            
  0            
274            
275 0           my $start_gene = $genes->[ $element_for->{$element_n}{start} ];
276              
277 0           my $end_gene = $genes->[ $element_for->{$element_n}{end} ];
278              
279 0           my $start_domain = @{ $element_for->{$element_n}{domains} }[0];
  0            
280 0           my $end_domain = @{ $element_for->{$element_n}{domains} }[-1];
  0            
281            
282 0           my $genomic_prot_begin = $start_gene->genomic_prot_begin
283             + $start_domain->begin;
284 0           my $genomic_prot_end = $end_gene->genomic_prot_begin
285             + $end_domain->end;
286              
287 0           my $size = $genomic_prot_end - $genomic_prot_begin + 1;
288              
289             # create protein sequence of element by domain sequence contatenation
290 0           my $cumulative_seq;
291 0           for my $domain (@{ $element_for->{ $element_n}{domains} }) {
  0            
292 0           $cumulative_seq .= $domain->protein_sequence;
293             }
294              
295 0           my $full_seq;
296 0 0         if ($start_gene->name eq $end_gene->name) {
297 0           my ($seq) = $start_gene->protein_sequence;
298 0           $full_seq = substr( $seq, ($start_domain->begin - 1),
299             ($end_domain->end - $start_domain->begin + 1) );
300             }
301              
302             else {
303 0           my ($seq1) = $start_gene->protein_sequence;
304 0           my $start_seq = substr ( $seq1, ($start_domain->begin - 1) );
305              
306 0           my ($seq2) = $end_gene->protein_sequence;
307 0           my $end_seq = substr ( $seq2, 0, ($end_domain->end) );
308              
309 0           $full_seq = $start_seq . $end_seq;
310             }
311              
312             my @gene_uuis
313 0           = map { $genes->[ $_ ]->uui }
314             $element_for->{$element_n}{start}..$element_for->{$element_n}{end}
315 0           ;
316              
317 0 0         if ($str eq 'module') {
318              
319             my $element = Module->new(
320             rank => ++$rank,
321             domains => $element_for->{$element_n}{domains}
322 0   0       // [],
323             gene_uuis => \@gene_uuis,
324             genomic_prot_begin => $genomic_prot_begin,
325             genomic_prot_end => $genomic_prot_end,
326             genomic_prot_coordinates => [$genomic_prot_begin,
327             $genomic_prot_end],
328             size => $size,
329             protein_sequence => $full_seq,
330             cumulative_protein_sequence => $cumulative_seq,
331             );
332            
333 0           push @elements, $element;
334             }
335              
336             else {
337              
338             my $element = Component->new(
339             rank => $element_n,
340             domains => $element_for->{$element_n}{domains}
341 0   0       // [],
342             gene_uuis => \@gene_uuis,
343             genomic_prot_begin => $genomic_prot_begin,
344             genomic_prot_end => $genomic_prot_end,
345             genomic_prot_coordinates => [$genomic_prot_begin,
346             $genomic_prot_end],
347             size => $size,
348             protein_sequence => $full_seq,
349             cumulative_protein_sequence => $cumulative_seq,
350             );
351            
352 0           push @elements, $element;
353             }
354             }
355              
356 0           return @elements;
357             }
358              
359 1     1   1745 no Moose::Role;
  1         2  
  1         10  
360             1;
361              
362             __END__
363              
364             =pod
365              
366             =head1 NAME
367              
368             Bio::Palantir::Roles::Modulable - Modulable Moose role for Module object construction
369              
370             =head1 VERSION
371              
372             version 0.200700
373              
374             =head1 SYNOPSIS
375              
376             # TODO
377              
378             =head1 DESCRIPTION
379              
380             # TODO
381              
382             =head1 AUTHOR
383              
384             Loic MEUNIER <lmeunier@uliege.be>
385              
386             =head1 COPYRIGHT AND LICENSE
387              
388             This software is copyright (c) 2019 by University of Liege / Unit of Eukaryotic Phylogenomics / Loic MEUNIER and Denis BAURAIN.
389              
390             This is free software; you can redistribute it and/or modify it under
391             the same terms as the Perl 5 programming language system itself.
392              
393             =cut