File Coverage

blib/lib/NBI/Opts.pm
Criterion Covered Total %
statement 152 173 87.8
branch 80 122 65.5
condition 0 2 0.0
subroutine 25 26 96.1
pod 17 17 100.0
total 274 340 80.5


line stmt bran cond sub pod time code
1             package NBI::Opts;
2             #ABSTRACT: A class for representing a the SLURM options for NBI::Slurm
3              
4 17     17   297209 use 5.012;
  17         56  
5 17     17   84 use warnings;
  17         25  
  17         1071  
6 17     17   86 use Carp qw(confess);
  17         23  
  17         963  
7 17     17   810 use Data::Dumper;
  17         10689  
  17         1086  
8             $Data::Dumper::Sortkeys = 1;
9 17     17   90 use File::Basename;
  17         37  
  17         2263  
10              
11             $NBI::Opts::VERSION = $NBI::Slurm::VERSION;
12              
13             my $SYSTEM_TEMPDIR = $ENV{'TMPDIR'} || $ENV{'TEMP'} || "/tmp";
14             require Exporter;
15             our @ISA = qw(Exporter);
16              
17             sub _yell {
18 17     17   6939 use Term::ANSIColor;
  17         113191  
  17         45665  
19 0     0   0 my $msg = shift @_;
20 0   0     0 my $col = shift @_ || "bold green";
21 0         0 say STDERR color($col), "[NBI::Opts]", color("reset"), " $msg";
22             }
23             sub new {
24 108     108 1 4060 my $class = shift @_;
25 108         316 my ($queue, $memory, $threads, $opts_array, $tmpdir, $hours, $email_address, $email_when, $files, $placeholder) = (undef, undef, undef, undef, undef, undef, undef, undef, undef);
26            
27             # Descriptive instantiation with parameters -param => value
28 108 100       346 if (substr($_[0], 0, 1) eq '-') {
29            
30 105         577 my %data = @_;
31              
32             # Try parsing
33 105         422 for my $i (keys %data) {
34            
35             # QUEUE
36 818 100       3675 if ($i =~ /^-queue/) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
37 104 50       273 next unless (defined $data{$i});
38 104         210 $queue = $data{$i};
39            
40              
41             # THREADS
42             } elsif ($i =~ /^-threads/) {
43 102 50       268 next unless (defined $data{$i});
44             # Check it's an integer
45 102 50       406 if ($data{$i} =~ /^\d+$/) {
46 102         230 $threads = $data{$i};
47             } else {
48 0         0 confess "ERROR NBI::Seq: -threads expects an integer\n";
49             }
50            
51            
52             # MEMORY
53             } elsif ($i =~ /^-memory/) {
54 102 50       242 next unless (defined $data{$i});
55 102         262 $memory = _mem_parse_mb($data{$i});
56            
57              
58             # TMPDIR
59             } elsif ($i =~ /^-tmpdir/) {
60 102 50       224 next unless (defined $data{$i});
61 102         257 $tmpdir = $data{$i};
62            
63             # MAIL ADDRESS
64             } elsif ($i =~ /^-(mail|email_address)/) {
65 101 50       245 next unless (defined $data{$i});
66 101         234 $email_address = $data{$i};
67            
68             # WHEN MAIL
69             } elsif ($i =~ /^-(when|email_type)/) {
70 101 50       243 next unless (defined $data{$i});
71 101         187 $email_when = $data{$i};
72            
73              
74             # OPTS ARRAY
75             } elsif ($i =~ /^-opts/) {
76 101 50       271 next unless (defined $data{$i});
77             # in this case we expect an array
78 101 50       255 if (ref($data{$i}) ne "ARRAY") {
79 0         0 confess "ERROR NBI::Seq: -opts expects an array\n";
80             }
81 101         253 $opts_array = $data{$i};
82            
83              
84             # TIME
85             } elsif ($i =~ /^-time/) {
86 102         266 $hours = _time_to_hour($data{$i});
87            
88             # PLACEHOLDER
89             } elsif ($i =~ /^-placeholder/) {
90             # check if placeholder contains special regex characters
91 1 50       2 if (not defined $data{$i}) {
92 0         0 confess "ERROR NBI::Seq: Placeholder cannot be empty\n";
93             }
94 1 50       3 if ($data{$i} =~ /[\*\+\?]/) {
95 0         0 confess "ERROR NBI::Seq: Placeholder cannot contain special regex characters\n";
96             }
97 1         2 $placeholder = $data{$i};
98            
99             # ARRAY
100             } elsif ($i =~ /^-files/) {
101             # expects ref to array
102 1 50       6 if (ref($data{$i}) ne "ARRAY") {
103 0         0 confess "ERROR NBI::Seq: -files expects an array\n";
104             } else {
105 1         1 $files = $data{$i};
106             }
107            
108             } else {
109 1         325 confess "ERROR NBI::Seq: Unknown parameter $i\n";
110             }
111             }
112             }
113            
114 107         371 my $self = bless {}, $class;
115            
116             # Set attributes
117 107 100       396 $self->queue = defined $queue ? $queue : "nbi-short";
118 107 100       305 $self->threads = defined $threads ? $threads : 1;
119 107 100       277 $self->memory = defined $memory ? $memory : 100;
120 107 100       310 $self->hours = defined $hours ? $hours : 1;
121 107 100       320 $self->tmpdir = defined $tmpdir ? $tmpdir : $SYSTEM_TEMPDIR;
122 107 100       315 $self->email_address = defined $email_address ? $email_address : undef;
123 107 100       289 $self->email_type = defined $email_when ? $email_when : "none";
124 107 100       354 $self->files = defined $files ? $files : [];
125 107 100       309 $self->placeholder = defined $placeholder ? $placeholder : "#FILE#";
126             # Set options
127 107 100       359 $self->opts = defined $$opts_array[0] ? $opts_array : [];
128            
129            
130            
131            
132              
133 107         425 return $self;
134            
135             }
136              
137              
138             sub queue : lvalue {
139             # Update queue
140 109     109 1 219 my ($self, $new_val) = @_;
141 109 50       276 $self->{queue} = $new_val if (defined $new_val);
142 109         340 return $self->{queue};
143             }
144              
145             sub threads : lvalue {
146             # Update threads
147 107     107 1 176 my ($self, $new_val) = @_;
148 107 50       215 $self->{threads} = $new_val if (defined $new_val);
149 107         295 return $self->{threads};
150             }
151              
152             sub memory : lvalue {
153             # Update memory
154 107     107 1 169 my ($self, $new_val) = @_;
155 107 50       229 $self->{memory} = _mem_parse_mb($new_val) if (defined $new_val);
156 107         239 return $self->{memory};
157             }
158              
159             sub email_address : lvalue {
160             # Update memory
161 107     107 1 202 my ($self, $new_val) = @_;
162 107 50       214 $self->{email_address} = $new_val if (defined $new_val);
163 107         272 return $self->{email_address};
164             }
165              
166             sub email_type : lvalue {
167             # Update memory
168 107     107 1 236 my ($self, $new_val) = @_;
169 107 50       239 $self->{email_type} = $new_val if (defined $new_val);
170 107         310 return $self->{email_type};
171             }
172              
173             # property files (list of files)
174             sub files : lvalue {
175             # Update files
176 108     108 1 183 my ($self, $new_val) = @_;
177 108 50       229 $self->{files} = $new_val if (defined $new_val);
178 108         230 return $self->{files};
179             }
180              
181             sub placeholder : lvalue {
182             # Update placeholder
183 312     312 1 656 my ($self, $new_val) = @_;
184 312 50       598 $self->{placeholder} = $new_val if (defined $new_val);
185 312         692 return $self->{placeholder};
186             }
187             sub is_array {
188             # Check if the job is an array
189 715     715 1 1181 my $self = shift @_;
190 715         1058 return scalar @{$self->{files}} > 0;
  715         2216  
191             }
192             sub hours : lvalue {
193             # Update memory
194 107     107 1 222 my ($self, $new_val) = @_;
195 107 50       229 $self->{hours} = _time_to_hour($new_val) if (defined $new_val);
196 107         230 return $self->{hours};
197             }
198              
199             sub tmpdir : lvalue {
200             # Update tmpdir
201 311     311 1 579 my ($self, $new_val) = @_;
202 311 50       650 $self->{tmpdir} = $new_val if (defined $new_val);
203 311         772 return $self->{tmpdir};
204             }
205              
206             sub opts : lvalue {
207             # Update opts
208 107     107 1 209 my ($self, $new_val) = @_;
209 107 50       742 if (not defined $self->{opts}) {
210 107         199 $self->{opts} = [];
211 107         263 return $self->{opts};
212             }
213             # check newval is an array
214 0 0       0 confess "ERROR NBI::Opts: opts must be an array, got $new_val\n" if (ref($new_val) ne "ARRAY");
215 0 0       0 $self->{opts} = $new_val if (defined $new_val);
216 0         0 return $self->{opts};
217             }
218             sub add_option {
219             # Add an option
220 1     1 1 882 my ($self, $new_val) = @_;
221 1         2 push @{$self->{opts}}, $new_val;
  1         4  
222 1         4 return $self->{opts};
223             }
224              
225             sub opts_count {
226             # Return the number of options
227 1     1 1 5 my $self = shift @_;
228 1 50       4 return defined $self->{opts} ? scalar @{$self->{opts}} : 0;
  1         6  
229             }
230              
231             sub view {
232             # Return a string representation of the object
233 203     203 1 774 my $self = shift @_;
234 203         351 my $str = " --- NBI::Opts object ---\n";
235 203         508 $str .= " queue:\t" . $self->{queue} . "\n";
236 203         428 $str .= " threads:\t" . $self->{threads} . "\n";
237 203         343 $str .= " memory MB:\t" . $self->{memory} . "\n";
238 203         1187 $str .= " time (h):\t" . $self->{hours} . "\n";
239 203         349 $str .= " tmpdir:\t" . $self->{tmpdir} . "\n";
240 203         337 $str .= " ---------------------------\n";
241 203         318 for my $o (@{$self->{opts}}) {
  203         418  
242 203 50       503 $str .= "#SBATCH $o\n" if defined $o;
243             }
244 203         674 return $str;
245             }
246              
247             sub header {
248             # Return a header for the script based on the options
249 203     203 1 328 my $self = shift @_;
250 203         333 my $str = "#!/bin/bash\n";
251             # Queue
252 203         507 $str .= "#SBATCH -p " . $self->{queue} . "\n";
253             # Nodes: 1
254 203         339 $str .= "#SBATCH -N 1\n";
255             # Time
256 203         557 $str .= "#SBATCH -t " . $self->timestring() . "\n";
257             # Memory
258 203         479 $str .= "#SBATCH --mem=" . $self->{memory} . "\n";
259             # Threads
260 203         346 $str .= "#SBATCH -c " . $self->{threads} . "\n";
261             # Mail
262 203 100       553 if (defined $self->{email_address}) {
263 202         407 $str .= "#SBATCH --mail-user=" . $self->{email_address} . "\n";
264 202         389 $str .= "#SBATCH --mail-type=" . $self->{email_type} . "\n";
265             }
266              
267             # Custom options
268 203         306 for my $o (@{$self->{opts}}) {
  203         444  
269 202 50       406 next if not defined $o;
270 202         377 $str .= "#SBATCH $o\n";
271             }
272              
273             # Job array
274 203 100       423 if ($self->is_array()) {
275 1         2 my $len = scalar @{$self->{files}} - 1;
  1         1  
276 1         2 $str .= "#SBATCH --array=0-$len\n";
277             }
278 203         1560 return $str;
279             }
280              
281             sub timestring {
282 203     203 1 294 my $self = shift @_;
283 203         382 my $hours = $self->{hours};
284 203         486 my $days = 0+ int($hours / 24);
285 203         364 $hours = $hours % 24;
286             # Format hours to be 2 digits
287 203         717 $hours = sprintf("%02d", $hours);
288 203         580 return "${days}-${hours}:00:00";
289             }
290              
291             sub _mem_parse_mb {
292 102     102   257 my $mem = shift @_;
293 102 50       364 if ($mem=~/^(\d+)$/) {
    0          
294             # bare number: interpret as MB
295 102         308 return $mem;
296             } elsif ($mem=~/^(\d+)\.?(MB?|GB?|TB?|KB?)$/i) {
297 0 0       0 if (substr(uc($2), 0, 1) eq "G") {
    0          
    0          
    0          
298 0         0 $mem = $1 * 1024;
299             } elsif (substr(uc($2), 0, 1) eq "T") {
300 0         0 $mem = $1 * 1024 * 1024;
301             } elsif (substr(uc($2), 0, 1) eq "M") {
302 0         0 $mem = $1;
303             } elsif (substr(uc($2), 0, 1) eq "K") {
304 0         0 $mem = int($1/1024);
305             } else {
306             # Consider MB
307 0         0 $mem = $1;
308             }
309             } else {
310 0         0 confess "ERROR NBI::Opts: Cannot parse memory value $mem\n";
311             }
312 0         0 return $mem;
313             }
314              
315             sub _time_to_hour {
316             # Get an integer (hours) or a string in the format \d+D \d+H \d+M \d+S
317 102     102   191 my $time = shift @_;
318 102         245 $time = uc($time);
319            
320 102 50       425 if ($time =~ /^(\d+)$/) {
321             # Got an integer
322 0         0 return $1;
323             } else {
324 102         168 my $hours = 0;
325 102         483 while ($time =~ /(\d+)([DHMS])/g) {
326 404         833 my $val = $1;
327 404         771 my $unit = $2;
328 404 100       996 if ($unit eq "D") {
    100          
    100          
    50          
329 101         431 $hours += $val * 24;
330             } elsif ($unit eq "H") {
331 101         322 $hours += $val;
332             } elsif ($unit eq "M") {
333 101         406 $hours += $val / 60;
334             } elsif ($unit eq "S") {
335 101         252 $hours += $val / 3600;
336             } else {
337 0         0 die "ERROR NBI::Opts: Cannot parse time value $time\n";
338             }
339             }
340 102         336 return $hours;
341             }
342             }
343              
344              
345             1;
346              
347             __END__