miga-base 0.6.1.0 → 0.6.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b7ff73c17fe13dbe5952bb607981bba291cf29d34c372157281339061036157
4
- data.tar.gz: d5af6ded80c32042dbc3463fab085f2bc5c58ea9fc8755224baeb8d08f527ff1
3
+ metadata.gz: 2c317237c4cd2f049b56af6021272208c206f1810052dde4b1b5b41394f37139
4
+ data.tar.gz: '066558242d9bcc1a96f166349815398296cdb976190853a18ca52bdafe84c263'
5
5
  SHA512:
6
- metadata.gz: b178fdfbe3ee23519fa7a6c23d15e6a9f393c1bea610124fa1ccf8245edbb3b95bb8ae9b46382eae3ca2e2664b1dd536fdd7a7c2b34c23cba35fb4e82c8a4123
7
- data.tar.gz: 96e1ff434584528ae8dcf0b6435cc1dbe74a312eb9a1a1dbab86d7464dfe50eb38bced778caf0ef46446c11864495af4b8902ab76ac9700716f5d82ddf8370d9
6
+ metadata.gz: 50b395740f68543e7aa79bf0e3631cb86248fffbcd9aac942e2d30681579a42e528d552638701f153605f68ea9da389762b6d068455e9b67cbfd9b66cde879c0
7
+ data.tar.gz: d20fd84e71bd4d7a8ebf5ff1060e12096d78477b662098e013b88ea9a19e54b4665a71bbfa0e49fd57df47a61b3a0780ed469fcb004b1384dd4e4f2304320666
@@ -74,9 +74,8 @@ class MiGA::Cli::Action::Daemon < MiGA::Cli::Action
74
74
  def perform
75
75
  p = cli.load_project
76
76
  d = MiGA::Daemon.new(p, cli[:json])
77
- [:latency, :maxjobs, :nodelist, :ppn, :shutdown_when_done].each do |k|
78
- d.runopts(k, cli[k]) unless cli[k].nil?
79
- end
77
+ dopts = %i[latency maxjobs nodelist ppn shutdown_when_done]
78
+ dopts.each { |k| d.runopts(k, cli[k]) }
80
79
  d.daemon(cli.operation, cli[:daemon_opts])
81
80
  end
82
81
  end
@@ -22,15 +22,15 @@ class MiGA::Daemon < MiGA::MiGA
22
22
  # Array of all spawned daemons.
23
23
  $_MIGA_DAEMON_LAIR = []
24
24
 
25
- # MiGA::Project in which the daemon is running.
25
+ # MiGA::Project in which the daemon is running
26
26
  attr_reader :project
27
- # Options used to setup the daemon.
27
+ # Options used to setup the daemon
28
28
  attr_reader :options
29
- # Array of jobs next to be executed.
29
+ # Array of jobs next to be executed
30
30
  attr_reader :jobs_to_run
31
- # Array of jobs currently running.
31
+ # Array of jobs currently running
32
32
  attr_reader :jobs_running
33
- # Integer indicating the current iteration.
33
+ # Integer indicating the current iteration
34
34
  attr_reader :loop_i
35
35
 
36
36
  ##
@@ -47,7 +47,7 @@ class MiGA::Daemon < MiGA::MiGA
47
47
  MiGA::Json.parse(
48
48
  json, default: File.expand_path('.miga_daemon.json', ENV['MIGA_HOME'])
49
49
  ).each { |k,v| runopts(k, v) }
50
- update_format_0 unless runopts(:format_version)
50
+ update_format_0
51
51
  @jobs_to_run = []
52
52
  @jobs_running = []
53
53
  @loop_i = -1
@@ -68,21 +68,22 @@ class MiGA::Daemon < MiGA::MiGA
68
68
  end
69
69
 
70
70
  ##
71
- # Launches the +task+ with options +opts+ (as command-line arguments).
72
- # Supported tasks include: start, stop, restart, status.
73
- def daemon(task, opts=[])
71
+ # Launches the +task+ with options +opts+ (as command-line arguments) and
72
+ # returns the process ID as an Integer. If +wait+ it waits for the process to
73
+ # complete, immediately returns otherwise.
74
+ # Supported tasks: start, stop, restart, status.
75
+ def daemon(task, opts = [], wait = true)
74
76
  MiGA.DEBUG "Daemon.daemon #{task} #{opts}"
75
77
  options = default_options
76
- opts.unshift(task)
78
+ opts.unshift(task.to_s)
77
79
  options[:ARGV] = opts
78
80
  # This additional degree of separation below was introduced so the Daemons
79
81
  # package doesn't kill the parent process in workflows.
80
82
  pid = fork do
81
- Daemons.run_proc("MiGA:#{project.name}", options) do
82
- loop { break unless in_loop }
83
- end
83
+ Daemons.run_proc("MiGA:#{project.name}", options) { while in_loop; end }
84
84
  end
85
- Process.wait pid
85
+ Process.wait(pid) if wait
86
+ pid
86
87
  end
87
88
 
88
89
  ##
@@ -130,13 +131,8 @@ class MiGA::Daemon < MiGA::MiGA
130
131
  # Traverse datasets
131
132
  def check_datasets
132
133
  project.each_dataset do |n, ds|
133
- if ds.nil?
134
- say "Warning: Dataset #{n} listed but not loaded, reloading project"
135
- project.load
136
- else
137
- to_run = ds.next_preprocessing(false)
138
- queue_job(:d, ds) unless to_run.nil?
139
- end
134
+ to_run = ds.next_preprocessing(false)
135
+ queue_job(:d, ds) unless to_run.nil?
140
136
  end
141
137
  end
142
138
 
@@ -202,9 +198,9 @@ class MiGA::Daemon < MiGA::MiGA
202
198
  @jobs_running.select! do |job|
203
199
  ongoing = case job[:job].to_s
204
200
  when 'd'
205
- not job[:ds].next_preprocessing(false).nil?
201
+ !job[:ds].next_preprocessing(false).nil?
206
202
  when 'p'
207
- not project.next_task(nil, false).nil?
203
+ !project.next_task(nil, false).nil?
208
204
  else
209
205
  (job[:ds].nil? ? project : job[:ds]).add_result(job[:job], false).nil?
210
206
  end
@@ -221,16 +217,13 @@ class MiGA::Daemon < MiGA::MiGA
221
217
  end
222
218
 
223
219
  ##
224
- # Retrieves the host index of an available node (if any), nil otherwise. If
225
- # #nodelist is not set, returns true as long as #maxjobs is not reached
220
+ # In SSH daemons, retrieve the host index of an available node, nil if none.
221
+ # In any other daemons, returns true as long as #maxjobs is not reached
226
222
  def next_host
227
- if nodelist.nil?
228
- return jobs_running.size < maxjobs
229
- end
223
+ return jobs_running.size < maxjobs if runopts(:type) != 'ssh'
230
224
  allk = (0 .. nodelist.size-1).to_a
231
225
  busyk = jobs_running.map { |k| k[:hostk] }
232
- availk = allk - busyk
233
- availk.empty? ? nil : availk.first
226
+ (allk - busyk).first
234
227
  end
235
228
 
236
229
  ##
@@ -248,7 +241,7 @@ class MiGA::Daemon < MiGA::MiGA
248
241
  project.load
249
242
  if loop_i == -1
250
243
  say '-----------------------------------'
251
- say 'MiGA:%s launched.' % project.name
244
+ say 'MiGA:%s launched' % project.name
252
245
  say '-----------------------------------'
253
246
  load_status
254
247
  @loop_i = 0
@@ -286,54 +279,52 @@ class MiGA::Daemon < MiGA::MiGA
286
279
  File.unlink(f) if File.exist? f
287
280
  end
288
281
 
289
- private
290
-
291
- def launch_job(job, hostk = nil)
292
- # Execute job
293
- case runopts(:type)
294
- when 'ssh'
295
- # Remote job
296
- job[:hostk] = hostk
297
- job[:cmd] = job[:cmd].miga_variables(host: nodelist[hostk])
298
- job[:pid] = spawn job[:cmd]
299
- Process.detach job[:pid] unless [nil, '', 0].include?(job[:pid])
300
- when 'bash'
301
- # Local job
302
- job[:pid] = spawn job[:cmd]
303
- Process.detach job[:pid] unless [nil, '', 0].include?(job[:pid])
304
- else
305
- # Schedule cluster job (qsub, msub, slurm)
306
- job[:pid] = `#{job[:cmd]}`.chomp
307
- end
282
+ ##
283
+ # Launch the job described by Hash +job+ to +hostk+-th host
284
+ def launch_job(job, hostk = nil)
285
+ # Execute job
286
+ case runopts(:type)
287
+ when 'ssh'
288
+ # Remote job
289
+ job[:hostk] = hostk
290
+ job[:cmd] = job[:cmd].miga_variables(host: nodelist[hostk])
291
+ job[:pid] = spawn job[:cmd]
292
+ Process.detach job[:pid] unless [nil, '', 0].include?(job[:pid])
293
+ when 'bash'
294
+ # Local job
295
+ job[:pid] = spawn job[:cmd]
296
+ Process.detach job[:pid] unless [nil, '', 0].include?(job[:pid])
297
+ else
298
+ # Schedule cluster job (qsub, msub, slurm)
299
+ job[:pid] = `#{job[:cmd]}`.chomp
300
+ end
308
301
 
309
- # Check if registered
310
- if [nil, '', 0].include? job[:pid]
311
- job[:pid] = nil
312
- @jobs_to_run << job
313
- say "Unsuccessful #{job[:task_name]}, rescheduling"
314
- else
315
- @jobs_running << job
316
- say "Spawned pid:#{job[:pid]}#{
317
- " to #{job[:hostk]}:#{nodelist[job[:hostk]]}" if job[:hostk]
318
- } for #{job[:task_name]}"
319
- end
302
+ # Check if registered
303
+ if [nil, '', 0].include? job[:pid]
304
+ job[:pid] = nil
305
+ @jobs_to_run << job
306
+ say "Unsuccessful #{job[:task_name]}, rescheduling"
307
+ else
308
+ @jobs_running << job
309
+ say "Spawned pid:#{job[:pid]}#{
310
+ " to #{job[:hostk]}:#{nodelist[job[:hostk]]}" if job[:hostk]
311
+ } for #{job[:task_name]}"
320
312
  end
313
+ end
321
314
 
322
- def update_format_0
323
- say 'Outdated daemon.json format, updating from version 0'
324
- {
325
- cmd: %w[script vars cpus log task_name],
326
- var: %w[key value],
327
- alive: %w[pid],
328
- kill: %w[pid]
329
- }.each do |k,v|
330
- runopts(
331
- k, sprintf(
332
- runopts(k).gsub(/%(\d+)\$d/, '%\\1$s'),
333
- *v.map{ |i| "{{#{i}}}" }
334
- )
335
- ) unless runopts(k).nil?
336
- end
337
- runopts(:format_version, 1)
315
+ ##
316
+ # Update from daemon JSON format 0 to the latest version
317
+ def update_format_0
318
+ {
319
+ cmd: %w[script vars cpus log task_name],
320
+ var: %w[key value],
321
+ alive: %w[pid],
322
+ kill: %w[pid]
323
+ }.each do |k,v|
324
+ runopts(
325
+ k, runopts(k).gsub(/%(\d+\$)?d/, '%\\1s') % v.map{ |i| "{{#{i}}}" }
326
+ ) if !runopts(k).nil? && runopts(k) =~ /%(\d+\$)?[ds]/
338
327
  end
328
+ runopts(:format_version, 1)
329
+ end
339
330
  end
@@ -4,13 +4,12 @@ require 'miga/result'
4
4
  require 'miga/dataset/base'
5
5
 
6
6
  ##
7
- # Helper module including specific functions to add dataset results.
7
+ # Helper module including specific functions to add dataset results
8
8
  module MiGA::Dataset::Result
9
-
10
9
  include MiGA::Dataset::Base
11
10
 
12
11
  ##
13
- # Get the result MiGA::Result in this dataset identified by the symbol +k+.
12
+ # Get the result MiGA::Result in this dataset identified by the symbol +k+
14
13
  def result(k)
15
14
  return nil if @@RESULT_DIRS[k.to_sym].nil?
16
15
  MiGA::Result.load(
@@ -19,11 +18,13 @@ module MiGA::Dataset::Result
19
18
  end
20
19
 
21
20
  ##
22
- # Get all the results (Array of MiGA::Result) in this dataset.
23
- def results ; @@RESULT_DIRS.keys.map{ |k| result k }.compact ; end
21
+ # Get all the results (Array of MiGA::Result) in this dataset
22
+ def results
23
+ @@RESULT_DIRS.keys.map { |k| result k }.compact
24
+ end
24
25
 
25
26
  ##
26
- # For each result executes the 2-ary +blk+ block: key symbol and MiGA::Result.
27
+ # For each result executes the 2-ary +blk+ block: key symbol and MiGA::Result
27
28
  def each_result(&blk)
28
29
  @@RESULT_DIRS.keys.each do |k|
29
30
  blk.call(k, result(k)) unless result(k).nil?
@@ -49,8 +50,9 @@ module MiGA::Dataset::Result
49
50
  r_pre = MiGA::Result.load("#{base}.json")
50
51
  return r_pre if (r_pre.nil? && !save) || !r_pre.nil?
51
52
  end
52
- r = File.exist?("#{base}.done") ?
53
- self.send("add_result_#{result_type}", base, opts) : nil
53
+ r = if File.exist?("#{base}.done")
54
+ self.send("add_result_#{result_type}", base, opts)
55
+ end
54
56
  unless r.nil?
55
57
  r.save
56
58
  pull_hook(:on_result_ready, result_type)
@@ -69,7 +71,7 @@ module MiGA::Dataset::Result
69
71
  # initial input. Passes +save+ to #add_result.
70
72
  def first_preprocessing(save = false)
71
73
  @first_processing ||= @@PREPROCESSING_TASKS.find do |t|
72
- not ignore_task?(t) and not add_result(t, save).nil?
74
+ !ignore_task?(t) && !add_result(t, save).nil?
73
75
  end
74
76
  end
75
77
 
@@ -82,22 +84,22 @@ module MiGA::Dataset::Result
82
84
  return nil if first.nil?
83
85
  @@PREPROCESSING_TASKS.each do |t|
84
86
  next if ignore_task? t
85
- if after_first and add_result(t, save).nil?
87
+ if after_first && add_result(t, save).nil?
86
88
  if (metadata["_try_#{t}"] || 0) > (project.metadata[:max_try] || 10)
87
89
  inactivate!
88
90
  return nil
89
91
  end
90
92
  return t
91
93
  end
92
- after_first = (after_first or (t==first))
94
+ after_first = (after_first || (t == first))
93
95
  end
94
96
  nil
95
97
  end
96
98
 
97
99
  ##
98
- # Are all the dataset-specific tasks done? Passes +save+ to #add_result.
100
+ # Are all the dataset-specific tasks done? Passes +save+ to #add_result
99
101
  def done_preprocessing?(save = false)
100
- !first_preprocessing(save).nil? and next_preprocessing(save).nil?
102
+ !first_preprocessing(save).nil? && next_preprocessing(save).nil?
101
103
  end
102
104
 
103
105
  ##
@@ -114,8 +116,8 @@ module MiGA::Dataset::Result
114
116
  state = 0
115
117
  next_task = next_preprocessing(save)
116
118
  @@PREPROCESSING_TASKS.each do |task|
117
- state = 1 if first_task==task
118
- state = 2 if !next_task.nil? and next_task==task
119
+ state = 1 if first_task == task
120
+ state = 2 if !next_task.nil? && next_task == task
119
121
  adv << state
120
122
  end
121
123
  adv
@@ -144,23 +146,23 @@ module MiGA::Dataset::Result
144
146
  def result_status(task)
145
147
  if first_preprocessing.nil?
146
148
  :ignore_empty
147
- elsif not get_result(task).nil?
149
+ elsif !get_result(task).nil?
148
150
  :complete
149
151
  elsif @@PREPROCESSING_TASKS.index(task) <
150
- @@PREPROCESSING_TASKS.index(first_preprocessing)
152
+ @@PREPROCESSING_TASKS.index(first_preprocessing)
151
153
  :-
152
154
  elsif ignore_task?(task)
153
- if not is_active?
155
+ if !is_active?
154
156
  :ignore_inactive
155
157
  elsif metadata["run_#{task}"]
156
158
  :ignore_force
157
- elsif task == :taxonomy and project.metadata[:ref_project].nil?
159
+ elsif task == :taxonomy && project.metadata[:ref_project].nil?
158
160
  :ignore_project
159
- elsif @@_EXCLUDE_NOREF_TASKS_H[task] && ! is_ref?
161
+ elsif @@_EXCLUDE_NOREF_TASKS_H[task] && !is_ref?
160
162
  :ignore_noref
161
- elsif @@_ONLY_MULTI_TASKS_H[task] && ! is_multi?
163
+ elsif @@_ONLY_MULTI_TASKS_H[task] && !is_multi?
162
164
  :ignore_multi
163
- elsif @@_ONLY_NONMULTI_TASKS_H[task] && ! is_nonmulti?
165
+ elsif @@_ONLY_NONMULTI_TASKS_H[task] && !is_nonmulti?
164
166
  :ignore_nonmulti
165
167
  else
166
168
  :ignore
@@ -169,7 +171,7 @@ module MiGA::Dataset::Result
169
171
  :pending
170
172
  end
171
173
  end
172
-
174
+
173
175
  ##
174
176
  # Clean-up all the stored distances, removing values for datasets no longer in
175
177
  # the project as reference datasets.
@@ -179,7 +181,7 @@ module MiGA::Dataset::Result
179
181
  return if r.nil?
180
182
  [:haai_db, :aai_db, :ani_db].each do |db_type|
181
183
  db = r.file_path(db_type)
182
- next if db.nil? or not File.size? db
184
+ next if db.nil? || !File.size?(db)
183
185
  sqlite_db = SQLite3::Database.new db
184
186
  table = db_type[-6..-4]
185
187
  val = sqlite_db.execute "select seq2 from #{table}"
@@ -192,220 +194,259 @@ module MiGA::Dataset::Result
192
194
 
193
195
  private
194
196
 
195
- ##
196
- # Add result type +:raw_reads+ at +base+ (no +_opts+ supported).
197
- def add_result_raw_reads(base, _opts)
198
- return nil unless result_files_exist?(base, '.1.fastq')
199
- r = MiGA::Result.new("#{base}.json")
200
- add_files_to_ds_result(r, name,
201
- ( result_files_exist?(base, '.2.fastq') ?
202
- {pair1: '.1.fastq', pair2: '.2.fastq'} :
203
- {single: '.1.fastq'} ))
204
- end
205
-
206
- ##
207
- # Add result type +:trimmed_reads+ at +base+ (no +_opts+ supported).
208
- def add_result_trimmed_reads(base, _opts)
209
- return nil unless result_files_exist?(base, ".1.clipped.fastq")
210
- r = MiGA::Result.new("#{base}.json")
211
- if result_files_exist?(base, ".2.clipped.fastq")
212
- r = add_files_to_ds_result(r, name,
213
- pair1: ".1.clipped.fastq", pair2: ".2.clipped.fastq",
214
- single: ".1.clipped.single.fastq")
197
+ ##
198
+ # Add result type +:raw_reads+ at +base+ (no +_opts+ supported)
199
+ def add_result_raw_reads(base, _opts)
200
+ return nil unless result_files_exist?(base, '.1.fastq')
201
+ r = MiGA::Result.new("#{base}.json")
202
+ add_files_to_ds_result(
203
+ r, name,
204
+ if result_files_exist?(base, '.2.fastq')
205
+ { pair1: '.1.fastq', pair2: '.2.fastq' }
215
206
  else
216
- r = add_files_to_ds_result(r, name, single: ".1.clipped.fastq")
207
+ { single: '.1.fastq' }
217
208
  end
218
- r.add_file(:trimming_sumary, "#{name}.1.fastq.trimmed.summary.txt")
219
- r
220
- end
209
+ )
210
+ end
221
211
 
222
- ##
223
- # Add result type +:read_quality+ at +base+ (no +_opts+ supported).
224
- def add_result_read_quality(base, _opts)
225
- return nil unless result_files_exist?(base, %w[.solexaqa .fastqc])
226
- r = MiGA::Result.new("#{base}.json")
227
- add_files_to_ds_result(r, name,
228
- solexaqa: ".solexaqa", fastqc: ".fastqc")
212
+ ##
213
+ # Add result type +:trimmed_reads+ at +base+ (no +_opts+ supported)
214
+ def add_result_trimmed_reads(base, _opts)
215
+ return nil unless result_files_exist?(base, '.1.clipped.fastq')
216
+ r = MiGA::Result.new("#{base}.json")
217
+ if result_files_exist?(base, '.2.clipped.fastq')
218
+ r = add_files_to_ds_result(r, name,
219
+ pair1: '.1.clipped.fastq',
220
+ pair2: '.2.clipped.fastq',
221
+ single: '.1.clipped.single.fastq')
222
+ else
223
+ r = add_files_to_ds_result(r, name, single: '.1.clipped.fastq')
229
224
  end
225
+ r.add_file(:trimming_sumary, "#{name}.1.fastq.trimmed.summary.txt")
226
+ r
227
+ end
230
228
 
231
- ##
232
- # Add result type +:trimmed_fasta+ at +base+ (no +_opts+ supported).
233
- def add_result_trimmed_fasta(base, _opts)
234
- return nil unless
235
- result_files_exist?(base, ".CoupledReads.fa") or
236
- result_files_exist?(base, ".SingleReads.fa") or
237
- result_files_exist?(base, %w[.1.fasta .2.fasta])
238
- r = MiGA::Result.new("#{base}.json")
239
- add_files_to_ds_result(r, name, coupled: ".CoupledReads.fa",
240
- single: ".SingleReads.fa", pair1: ".1.fasta", pair2: ".2.fasta")
241
- end
229
+ ##
230
+ # Add result type +:read_quality+ at +base+ (no +_opts+ supported)
231
+ def add_result_read_quality(base, _opts)
232
+ return nil unless result_files_exist?(base, %w[.solexaqa .fastqc])
233
+ r = MiGA::Result.new("#{base}.json")
234
+ add_files_to_ds_result(r, name, solexaqa: '.solexaqa', fastqc: '.fastqc')
235
+ end
242
236
 
243
- ##
244
- # Add result type +:assembly+ at +base+. Hash +opts+ supports
245
- # +is_clean: Boolean+.
246
- def add_result_assembly(base, opts)
247
- return nil unless result_files_exist?(base, ".LargeContigs.fna")
248
- r = MiGA::Result.new("#{base}.json")
249
- r = add_files_to_ds_result(r, name, largecontigs: ".LargeContigs.fna",
250
- allcontigs: ".AllContigs.fna", assembly_data: '')
251
- opts[:is_clean] ||= false
252
- r.clean! if opts[:is_clean]
253
- unless r.clean?
254
- MiGA::MiGA.clean_fasta_file(r.file_path :largecontigs)
255
- r.clean!
256
- end
257
- r
258
- end
237
+ ##
238
+ # Add result type +:trimmed_fasta+ at +base+ (no +_opts+ supported)
239
+ def add_result_trimmed_fasta(base, _opts)
240
+ return nil unless
241
+ result_files_exist?(base, '.CoupledReads.fa') ||
242
+ result_files_exist?(base, '.SingleReads.fa') ||
243
+ result_files_exist?(base, %w[.1.fasta .2.fasta])
244
+ r = MiGA::Result.new("#{base}.json")
245
+ add_files_to_ds_result(
246
+ r, name,
247
+ coupled: '.CoupledReads.fa',
248
+ single: '.SingleReads.fa',
249
+ pair1: '.1.fasta',
250
+ pair2: '.2.fasta')
251
+ end
259
252
 
260
- ##
261
- # Add result type +:cds+ at +base+. Hash +opts+ supports +is_clean: Boolean+
262
- def add_result_cds(base, opts)
263
- return nil unless result_files_exist?(base, %w[.faa])
264
- r = MiGA::Result.new("#{base}.json")
265
- r = add_files_to_ds_result(r, name, proteins: ".faa", genes: ".fna",
266
- gff2: ".gff2", gff3: ".gff3", tab: ".tab")
267
- opts[:is_clean] ||= false
268
- r.clean! if opts[:is_clean]
269
- unless r.clean?
270
- MiGA::MiGA.clean_fasta_file(r.file_path :proteins)
271
- MiGA::MiGA.clean_fasta_file(r.file_path :genes) if r.file_path :genes
272
- r.clean!
273
- end
274
- r
253
+ ##
254
+ # Add result type +:assembly+ at +base+. Hash +opts+ supports
255
+ # +is_clean: Boolean+.
256
+ def add_result_assembly(base, opts)
257
+ return nil unless result_files_exist?(base, '.LargeContigs.fna')
258
+ r = MiGA::Result.new("#{base}.json")
259
+ r = add_files_to_ds_result(
260
+ r, name,
261
+ largecontigs: '.LargeContigs.fna',
262
+ allcontigs: '.AllContigs.fna',
263
+ assembly_data: '')
264
+ opts[:is_clean] ||= false
265
+ r.clean! if opts[:is_clean]
266
+ unless r.clean?
267
+ MiGA::MiGA.clean_fasta_file(r.file_path(:largecontigs))
268
+ r.clean!
275
269
  end
270
+ r
271
+ end
276
272
 
277
- ##
278
- # Add result type +:essential_genes+ at +base+ (no +_opts+ supported).
279
- def add_result_essential_genes(base, _opts)
280
- return nil unless result_files_exist?(base, %w[.ess.faa .ess .ess/log])
281
- r = MiGA::Result.new("#{base}.json")
282
- add_files_to_ds_result(r, name, ess_genes: '.ess.faa',
283
- collection: '.ess', report: '.ess/log', alignments: '.ess/proteins.aln')
273
+ ##
274
+ # Add result type +:cds+ at +base+. Hash +opts+ supports +is_clean: Boolean+
275
+ def add_result_cds(base, opts)
276
+ return nil unless result_files_exist?(base, %w[.faa])
277
+ r = MiGA::Result.new("#{base}.json")
278
+ r = add_files_to_ds_result(
279
+ r, name,
280
+ proteins: '.faa',
281
+ genes: '.fna',
282
+ gff2: '.gff2',
283
+ gff3: '.gff3',
284
+ tab: '.tab')
285
+ opts[:is_clean] ||= false
286
+ r.clean! if opts[:is_clean]
287
+ unless r.clean?
288
+ MiGA::MiGA.clean_fasta_file(r.file_path(:proteins))
289
+ MiGA::MiGA.clean_fasta_file(r.file_path(:genes)) if r.file_path(:genes)
290
+ r.clean!
284
291
  end
292
+ r
293
+ end
285
294
 
286
- ##
287
- # Add result type +:ssu+ at +base+. Hash +opts+ supports +is_clean: Boolean+
288
- def add_result_ssu(base, opts)
289
- return MiGA::Result.new("#{base}.json") if result(:assembly).nil?
290
- return nil unless result_files_exist?(base, ".ssu.fa")
291
- r = MiGA::Result.new("#{base}.json")
292
- r = add_files_to_ds_result(r, name, longest_ssu_gene: ".ssu.fa",
293
- gff: ".ssu.gff", all_ssu_genes: ".ssu.all.fa")
294
- opts[:is_clean] ||= false
295
- r.clean! if opts[:is_clean]
296
- unless r.clean?
297
- MiGA::MiGA.clean_fasta_file(r.file_path :longest_ssu_gene)
298
- r.clean!
299
- end
300
- r
295
+ ##
296
+ # Add result type +:essential_genes+ at +base+ (no +_opts+ supported).
297
+ def add_result_essential_genes(base, _opts)
298
+ return nil unless result_files_exist?(base, %w[.ess.faa .ess .ess/log])
299
+ r = MiGA::Result.new("#{base}.json")
300
+ add_files_to_ds_result(
301
+ r, name,
302
+ ess_genes: '.ess.faa',
303
+ collection: '.ess',
304
+ report: '.ess/log',
305
+ alignments: '.ess/proteins.aln')
306
+ end
307
+
308
+ ##
309
+ # Add result type +:ssu+ at +base+. Hash +opts+ supports +is_clean: Boolean+
310
+ def add_result_ssu(base, opts)
311
+ return MiGA::Result.new("#{base}.json") if result(:assembly).nil?
312
+ return nil unless result_files_exist?(base, '.ssu.fa')
313
+ r = MiGA::Result.new("#{base}.json")
314
+ r = add_files_to_ds_result(
315
+ r, name,
316
+ longest_ssu_gene: '.ssu.fa',
317
+ gff: '.ssu.gff',
318
+ all_ssu_genes: '.ssu.all.fa')
319
+ opts[:is_clean] ||= false
320
+ r.clean! if opts[:is_clean]
321
+ unless r.clean?
322
+ MiGA::MiGA.clean_fasta_file(r.file_path(:longest_ssu_gene))
323
+ r.clean!
301
324
  end
325
+ r
326
+ end
302
327
 
303
- ##
304
- # Add result type +:mytaxa+ at +base+ (no +_opts+ supported).
305
- def add_result_mytaxa(base, _opts)
306
- if is_multi?
307
- return nil unless result_files_exist?(base, '.mytaxa') or
308
- result_files_exist?(base, '.nomytaxa.txt')
309
- r = MiGA::Result.new("#{base}.json")
310
- add_files_to_ds_result(
311
- r, name,
312
- mytaxa: '.mytaxa', blast: '.blast',
313
- mytaxain: '.mytaxain', nomytaxa: '.nomytaxa.txt',
314
- species: '.mytaxa.Species.txt', genus: '.mytaxa.Genus.txt',
315
- phylum: '.mytaxa.Phylum.txt', innominate: '.mytaxa.innominate',
316
- kronain: '.mytaxa.krona', krona: '.html'
317
- )
318
- else
319
- MiGA::Result.new("#{base}.json")
320
- end
328
+ ##
329
+ # Add result type +:mytaxa+ at +base+ (no +_opts+ supported)
330
+ def add_result_mytaxa(base, _opts)
331
+ if is_multi?
332
+ return nil unless
333
+ result_files_exist?(base, '.mytaxa') ||
334
+ result_files_exist?(base, '.nomytaxa.txt')
335
+ r = MiGA::Result.new("#{base}.json")
336
+ add_files_to_ds_result(
337
+ r, name,
338
+ mytaxa: '.mytaxa',
339
+ blast: '.blast',
340
+ mytaxain: '.mytaxain',
341
+ nomytaxa: '.nomytaxa.txt',
342
+ species: '.mytaxa.Species.txt',
343
+ genus: '.mytaxa.Genus.txt',
344
+ phylum: '.mytaxa.Phylum.txt',
345
+ innominate: '.mytaxa.innominate',
346
+ kronain: '.mytaxa.krona',
347
+ krona: '.html')
348
+ else
349
+ MiGA::Result.new("#{base}.json")
321
350
  end
351
+ end
322
352
 
323
- ##
324
- # Add result type +:mytaxa_scan+ at +base+ (no +_opts+ supported).
325
- def add_result_mytaxa_scan(base, _opts)
326
- if is_nonmulti?
327
- return nil unless
328
- result_files_exist?(base, %w[.pdf .mytaxa]) or
329
- result_files_exist?(base, '.nomytaxa.txt')
330
- r = MiGA::Result.new("#{base}.json")
331
- add_files_to_ds_result(r, name, nomytaxa: '.nomytaxa.txt',
332
- mytaxa: '.mytaxa', report: '.pdf', regions_archive: '.reg.tar',
333
- # Intermediate / Deprecated
334
- blast: '.blast', mytaxain: '.mytaxain', wintax: '.wintax',
335
- gene_ids: '.wintax.genes', region_ids: '.wintax.regions',
336
- regions: '.reg')
337
- else
338
- MiGA::Result.new("#{base}.json")
339
- end
353
+ ##
354
+ # Add result type +:mytaxa_scan+ at +base+ (no +_opts+ supported)
355
+ def add_result_mytaxa_scan(base, _opts)
356
+ if is_nonmulti?
357
+ return nil unless
358
+ result_files_exist?(base, %w[.pdf .mytaxa]) ||
359
+ result_files_exist?(base, '.nomytaxa.txt')
360
+ r = MiGA::Result.new("#{base}.json")
361
+ add_files_to_ds_result(
362
+ r, name,
363
+ nomytaxa: '.nomytaxa.txt',
364
+ mytaxa: '.mytaxa',
365
+ report: '.pdf',
366
+ regions_archive: '.reg.tar',
367
+ # Intermediate / Deprecated:
368
+ blast: '.blast',
369
+ mytaxain: '.mytaxain',
370
+ wintax: '.wintax',
371
+ gene_ids: '.wintax.genes',
372
+ region_ids: '.wintax.regions',
373
+ regions: '.reg')
374
+ else
375
+ MiGA::Result.new("#{base}.json")
340
376
  end
377
+ end
341
378
 
342
- ##
343
- # Add result type +:distances+ at +base+ (no +_opts+ supported).
344
- def add_result_distances(base, _opts)
345
- if is_nonmulti?
346
- if is_ref?
347
- add_result_distances_ref(base)
348
- else
349
- add_result_distances_nonref(base)
350
- end
379
+ ##
380
+ # Add result type +:distances+ at +base+ (no +_opts+ supported)
381
+ def add_result_distances(base, _opts)
382
+ if is_nonmulti?
383
+ if is_ref?
384
+ add_result_distances_ref(base)
351
385
  else
352
- add_result_distances_multi(base)
386
+ add_result_distances_nonref(base)
353
387
  end
388
+ else
389
+ add_result_distances_multi(base)
354
390
  end
391
+ end
355
392
 
356
- ##
357
- # Add result type +:taxonomy+ at +base+ (no +_opts+ supported).
358
- def add_result_taxonomy(base, _opts)
359
- add_result_distances_nonref(base)
360
- end
361
-
362
- ##
363
- # Add result type +:stats+ at +base+ (no +_opts+ supported).
364
- def add_result_stats(base, _opts)
365
- MiGA::Result.new("#{base}.json")
366
- end
393
+ ##
394
+ # Add result type +:taxonomy+ at +base+ (no +_opts+ supported)
395
+ def add_result_taxonomy(base, _opts)
396
+ add_result_distances_nonref(base)
397
+ end
367
398
 
368
- ##
369
- # Add result type +:distances+ for _multi_ datasets at +base+.
370
- def add_result_distances_multi(base)
371
- MiGA::Result.new("#{base}.json")
372
- end
399
+ ##
400
+ # Add result type +:stats+ at +base+ (no +_opts+ supported)
401
+ def add_result_stats(base, _opts)
402
+ MiGA::Result.new("#{base}.json")
403
+ end
373
404
 
374
- ##
375
- # Add result type +:distances+ for _nonmulti_ reference datasets at +base+.
376
- def add_result_distances_ref(base)
377
- pref = File.dirname(base)
378
- return nil unless
379
- File.exist?("#{pref}/01.haai/#{name}.db")
380
- r = MiGA::Result.new("#{base}.json")
381
- r.add_files(haai_db: "01.haai/#{name}.db", aai_db: "02.aai/#{name}.db",
382
- ani_db: "03.ani/#{name}.db")
383
- r
384
- end
405
+ ##
406
+ # Add result type +:distances+ for _multi_ datasets at +base+
407
+ def add_result_distances_multi(base)
408
+ MiGA::Result.new("#{base}.json")
409
+ end
385
410
 
386
- ##
387
- # Add result type +:distances+ for _nonmulti_ query datasets at +base+.
388
- def add_result_distances_nonref(base)
389
- return nil unless
390
- result_files_exist?(base, %w[.aai-medoids.tsv .aai.db]) or
391
- result_files_exist?(base, %w[.ani-medoids.tsv .ani.db])
392
- r = MiGA::Result.new("#{base}.json")
393
- add_files_to_ds_result(
394
- r, name,
395
- aai_medoids: '.aai-medoids.tsv',
396
- haai_db: '.haai.db', aai_db: '.aai.db', ani_medoids: '.ani-medoids.tsv',
397
- ani_db: '.ani.db', ref_tree: '.nwk', ref_tree_pdf: '.nwk.pdf',
398
- intax_test: '.intax.txt'
411
+ ##
412
+ # Add result type +:distances+ for _nonmulti_ reference datasets at +base+
413
+ def add_result_distances_ref(base)
414
+ pref = File.dirname(base)
415
+ return nil unless File.exist?("#{pref}/01.haai/#{name}.db")
416
+ MiGA::Result.new("#{base}.json").tap do |r|
417
+ r.add_files(
418
+ haai_db: "01.haai/#{name}.db",
419
+ aai_db: "02.aai/#{name}.db",
420
+ ani_db: "03.ani/#{name}.db"
399
421
  )
400
422
  end
423
+ end
401
424
 
402
- ##
403
- # Add files in +rel_files+ Hash to the result +r+ with dataset name +name+.
404
- def add_files_to_ds_result(r, name, rel_files)
405
- files = {}
406
- rel_files.each{ |k,v| files[k] = name + v }
407
- r.add_files(files)
408
- r
409
- end
425
+ ##
426
+ # Add result type +:distances+ for _nonmulti_ query datasets at +base+
427
+ def add_result_distances_nonref(base)
428
+ return nil unless
429
+ result_files_exist?(base, %w[.aai-medoids.tsv .aai.db]) ||
430
+ result_files_exist?(base, %w[.ani-medoids.tsv .ani.db])
431
+ r = MiGA::Result.new("#{base}.json")
432
+ add_files_to_ds_result(
433
+ r, name,
434
+ aai_medoids: '.aai-medoids.tsv',
435
+ haai_db: '.haai.db',
436
+ aai_db: '.aai.db',
437
+ ani_medoids: '.ani-medoids.tsv',
438
+ ani_db: '.ani.db',
439
+ ref_tree: '.nwk',
440
+ ref_tree_pdf: '.nwk.pdf',
441
+ intax_test: '.intax.txt')
442
+ end
410
443
 
444
+ ##
445
+ # Add files in +rel_files+ Hash to the result +r+ with dataset name +name+
446
+ def add_files_to_ds_result(r, name, rel_files)
447
+ files = {}
448
+ rel_files.each { |k, v| files[k] = name + v }
449
+ r.add_files(files)
450
+ r
451
+ end
411
452
  end
@@ -25,7 +25,7 @@ module MiGA::Project::Dataset
25
25
  end
26
26
 
27
27
  ##
28
- # Returns MiGA::Dataset.
28
+ # Returns MiGA::Dataset
29
29
  def dataset(name)
30
30
  name = name.miga_name
31
31
  return nil unless MiGA::Dataset.exist?(self, name)
@@ -10,7 +10,7 @@ module MiGA
10
10
  # - Float representing the major.minor version.
11
11
  # - Integer representing gem releases of the current version.
12
12
  # - Integer representing minor changes that require new version number.
13
- VERSION = [0.6, 1, 0]
13
+ VERSION = [0.6, 2, 0]
14
14
 
15
15
  ##
16
16
  # Nickname for the current major.minor version.
@@ -18,7 +18,7 @@ module MiGA
18
18
 
19
19
  ##
20
20
  # Date of the current gem release.
21
- VERSION_DATE = Date.new(2020, 3, 10)
21
+ VERSION_DATE = Date.new(2020, 3, 11)
22
22
 
23
23
  ##
24
24
  # Reference of MiGA.
@@ -71,14 +71,6 @@ class CommonTest < Test::Unit::TestCase
71
71
  FileUtils.rm_rf $tmp
72
72
  end
73
73
 
74
- def test_tabulate
75
- tab = MiGA::MiGA.tabulate(%w[a b], [%w[123 45], %w[678 90]])
76
- assert_equal(' a b ', tab[0])
77
- assert_equal(' - - ', tab[1])
78
- assert_equal('123 45', tab[2])
79
- assert_equal('678 90', tab[3])
80
- end
81
-
82
74
  def test_miga_name
83
75
  assert_equal('Xa sp. C', 'Xa_sp__C'.unmiga_name)
84
76
  assert_equal('X_______', 'X^*.!{}!'.miga_name)
@@ -11,7 +11,7 @@ class DaemonTest < Test::Unit::TestCase
11
11
  daemon_json = File.expand_path('.miga_daemon.json', ENV['MIGA_HOME'])
12
12
  File.open(daemon_json, 'w') do |fh|
13
13
  fh.puts '{"maxjobs":1,"ppn":1,"latency":2,"varsep":" ",
14
- "var":"{{key}}={{value}}","cmd":"{{task_name}}",
14
+ "var":"{{key}}={{value}}","cmd":"echo {{task_name}} >/dev/null",
15
15
  "alive":"echo 1 # {{pid}}","type":"bash","format_version":1}'
16
16
  end
17
17
  $p1 = MiGA::Project.new(File.expand_path('project1', $tmp))
@@ -23,7 +23,30 @@ class DaemonTest < Test::Unit::TestCase
23
23
  ENV['MIGA_HOME'] = nil
24
24
  end
25
25
 
26
+ def helper_datasets_with_results(n = 1)
27
+ p1 = $p1
28
+ Array.new(n) do |i|
29
+ d = "d#{i}"
30
+ FileUtils.touch(File.expand_path(
31
+ "data/02.trimmed_reads/#{d}.1.clipped.fastq", p1.path
32
+ ))
33
+ FileUtils.touch(File.expand_path(
34
+ "data/02.trimmed_reads/#{d}.done", p1.path
35
+ ))
36
+ p1.add_dataset(MiGA::Dataset.new(p1, d, true).name).tap do |ds|
37
+ ds.first_preprocessing(true)
38
+ end
39
+ end
40
+ end
41
+
26
42
  def test_check_project
43
+ d1 = $d1
44
+ helper_datasets_with_results.first.inactivate!
45
+ out = capture_stdout { d1.check_project }
46
+ assert(out.string =~ /Queueing miga-project:p/)
47
+ assert_equal(1, d1.jobs_to_run.size)
48
+ assert_equal(:p, d1.jobs_to_run.first[:job])
49
+ assert_equal('project1:p:miga-project', d1.get_job(:p)[:task_name])
27
50
  end
28
51
 
29
52
  def test_check_datasets
@@ -48,7 +71,7 @@ class DaemonTest < Test::Unit::TestCase
48
71
  end
49
72
  assert(out.string =~ /Queueing #{ds.name}:d/)
50
73
  assert_equal(1, d.jobs_to_run.size)
51
- assert_equal('project1:d:ds1', d.jobs_to_run.first[:cmd])
74
+ assert_equal('echo project1:d:ds1 >/dev/null', d.jobs_to_run.first[:cmd])
52
75
  assert_equal(d.jobs_to_run.first, d.get_job(:d, ds))
53
76
  end
54
77
 
@@ -74,11 +97,16 @@ class DaemonTest < Test::Unit::TestCase
74
97
  d.runopts(:latency, 0, true)
75
98
  assert_equal(0, d.latency)
76
99
  omit_if($jruby_tests, 'JRuby doesn\'t implement fork.')
77
- $child = fork { d.start(['--shush']) }
100
+ child = $child = d.daemon(:start, ['--shush'])
101
+ assert(child.is_a? Integer)
102
+ assert(child != 0, 'The daemond process should have non-zero PID')
103
+ assert_equal(0, `ps -p "#{child}" -o ppid=`.strip.to_i,
104
+ 'The daemon process should be detached')
78
105
  sleep(3)
79
- dpath = File.expand_path("daemon/MiGA:#{p.name}",p.path)
106
+ dpath = File.join(p.path, 'daemon', "MiGA:#{p.name}")
80
107
  assert(File.exist?("#{dpath}.pid"))
81
108
  out = capture_stdout { d.stop }
109
+ assert_raise(Errno::ESRCH) { Process.kill(0, child) }
82
110
  assert_equal('', out.string)
83
111
  assert(!File.exist?("#{dpath}.pid"))
84
112
  assert(File.exist?("#{dpath}.output"))
@@ -90,7 +118,11 @@ class DaemonTest < Test::Unit::TestCase
90
118
  assert(l[3] =~ /Probing running jobs\n/)
91
119
  end
92
120
  ensure
93
- Process.kill('KILL', $child) unless $child.nil?
121
+ begin
122
+ Process.kill('KILL', $child) if !$child.nil?
123
+ rescue Errno::ESRCH
124
+ false
125
+ end
94
126
  end
95
127
 
96
128
  def test_last_alive
@@ -102,18 +134,19 @@ class DaemonTest < Test::Unit::TestCase
102
134
  end
103
135
 
104
136
  def test_options
105
- assert_respond_to($d1, :default_options)
106
- assert_equal(:normal, $d1.default_options[:dir_mode])
107
- assert_equal(2, $d1.runopts(:latency))
108
- assert_equal(1, $d1.maxjobs)
109
- assert_equal(2, $d1.latency)
110
- assert_equal(1, $d1.ppn)
111
- $d1.runopts(:alo, :ha)
112
- assert_equal(:ha, $d1.runopts(:alo))
113
- $d1.runopts(:maxjobs, '1')
114
- assert_equal(1, $d1.maxjobs)
115
- assert_raise { $d1.runopts(:latency, '!') }
116
- assert_equal('bash', $d1.runopts(:type))
137
+ d1 = $d1
138
+ assert_respond_to(d1, :default_options)
139
+ assert_equal(:normal, d1.default_options[:dir_mode])
140
+ assert_equal(2, d1.runopts(:latency))
141
+ assert_equal(1, d1.maxjobs)
142
+ assert_equal(2, d1.latency)
143
+ assert_equal(1, d1.ppn)
144
+ d1.runopts(:alo, :ha)
145
+ assert_equal(:ha, d1.runopts(:alo))
146
+ d1.runopts(:maxjobs, '1')
147
+ assert_equal(1, d1.maxjobs)
148
+ assert_raise { d1.runopts(:latency, '!') }
149
+ assert_equal('bash', d1.runopts(:type))
117
150
  end
118
151
 
119
152
  def test_say
@@ -130,4 +163,153 @@ class DaemonTest < Test::Unit::TestCase
130
163
  assert_nil(d.last_alive)
131
164
  end
132
165
 
166
+ def test_maxjobs_json
167
+ d1 = $d1
168
+ helper_datasets_with_results(3)
169
+ assert_equal(0, d1.jobs_running.size)
170
+ assert_equal(0, d1.jobs_to_run.size)
171
+ capture_stdout { d1.in_loop }
172
+ assert_equal(1, d1.jobs_running.size)
173
+ assert_equal(2, d1.jobs_to_run.size)
174
+ end
175
+
176
+ def test_maxjobs_runopts
177
+ d1 = $d1
178
+ helper_datasets_with_results(3)
179
+ d1.runopts(:maxjobs, 2)
180
+ assert_equal(0, d1.jobs_running.size)
181
+ assert_equal(0, d1.jobs_to_run.size)
182
+ capture_stdout { d1.in_loop }
183
+ assert_equal(2, d1.jobs_running.size)
184
+ assert_equal(1, d1.jobs_to_run.size)
185
+ end
186
+
187
+ def test_load_status
188
+ d1 = $d1
189
+ p1 = $p1
190
+ assert_equal(0, d1.jobs_running.size)
191
+ assert_nil(d1.load_status)
192
+ assert_equal(0, d1.jobs_running.size)
193
+ p1.add_dataset(MiGA::Dataset.new(p1, 'd1').name)
194
+ f = File.join(p1.path, 'daemon', 'status.json')
195
+ File.open(f, 'w') do |h|
196
+ h.puts '{"jobs_running":[{"ds":1,"ds_name":"d1"},{}],"jobs_to_run":[]}'
197
+ end
198
+ out = capture_stdout { d1.load_status }
199
+ assert_equal(2, d1.jobs_running.size)
200
+ assert(out.string =~ /Loading previous status/)
201
+ assert_equal(MiGA::Dataset, d1.jobs_running[0][:ds].class)
202
+ assert_nil(d1.jobs_running[1][:ds])
203
+ end
204
+
205
+ def test_flush
206
+ d1 = $d1
207
+ p1 = $p1
208
+ helper_datasets_with_results
209
+ p1.add_dataset(MiGA::Dataset.new(p1, 'd1').name)
210
+ MiGA::Project.RESULT_DIRS.keys.each { |i| p1.metadata["run_#{i}"] = false }
211
+ f = File.join(p1.path, 'daemon', 'status.json')
212
+ File.open(f, 'w') do |h|
213
+ h.puts '{"jobs_running":' \
214
+ '[{"job":"p"},{"job":"d","ds":1,"ds_name":"d1"},' \
215
+ '{"job":"trimmed_reads","ds":1,"ds_name":"d0"}]' \
216
+ ',"jobs_to_run":[]}'
217
+ end
218
+ capture_stdout { d1.load_status }
219
+ assert_equal(3, d1.jobs_running.size)
220
+ out = capture_stdout { d1.flush! }
221
+ assert(out.string =~ /Completed pid:/)
222
+ assert_equal([], d1.jobs_running)
223
+ end
224
+
225
+ def test_next_host
226
+ d1 = $d1
227
+ f = File.join($tmp, 'nodes.txt')
228
+ File.open(f, 'w') { |h| h.puts 'localhost' }
229
+ assert_equal(true, d1.next_host)
230
+ out = capture_stdout { d1.runopts(:nodelist, f) }
231
+ assert(out.string =~ /Reading node list:/)
232
+ assert_equal(true, d1.next_host)
233
+ d1.runopts(:type, 'ssh')
234
+ assert_equal(0, d1.next_host)
235
+ f = File.join($p1.path, 'daemon', 'status.json')
236
+ File.open(f, 'w') do |h|
237
+ h.puts '{"jobs_running":[{"job":"p","hostk":0}], "jobs_to_run":[]}'
238
+ end
239
+ capture_stdout { d1.load_status }
240
+ assert_nil(d1.next_host)
241
+ end
242
+
243
+ def test_shutdown_when_done
244
+ $d1.runopts(:shutdown_when_done, true)
245
+ out = capture_stdout { assert(!$d1.in_loop) }
246
+ assert(out.string =~ /Nothing else to do/)
247
+ end
248
+
249
+ def test_update_format_0
250
+ f = File.join($tmp, 'daemon.json')
251
+ File.open(f, 'w') do |fh|
252
+ fh.puts '{"maxjobs":1,"ppn":1,"latency":2,"varsep":" ",
253
+ "var":"%1$s=%1$s","cmd":"echo %1$s","alive":"echo %1$d","type":"bash"}'
254
+ end
255
+ d2 = MiGA::Daemon.new($p1, f)
256
+ assert_equal('echo {{script}}', d2.runopts(:cmd))
257
+ assert_equal('echo {{pid}}', d2.runopts(:alive))
258
+ end
259
+
260
+ def test_launch_job_bash
261
+ t = File.join($tmp, 'launch_job_bash')
262
+ $d1.runopts(:type, 'bash')
263
+ $d1.runopts(:cmd, "echo {{task_name}} > '#{t}'")
264
+ helper_daemon_launch_job
265
+ assert_equal("project1:p:miga-project\n", File.read(t))
266
+ end
267
+
268
+ def test_launch_job_ssh
269
+ d1 = $d1
270
+ t = File.join($tmp, 'launch_job_ssh')
271
+ d1.runopts(:type, 'ssh')
272
+ d1.runopts(:cmd, "echo {{task_name}} > '#{t}'")
273
+ f = File.join($tmp, 'nodes.txt')
274
+ File.open(f, 'w') { |h| h.puts 'localhost' }
275
+ assert_raise('Unset environment variable: $MIGA_TEST_NODELIST') do
276
+ d1.runopts(:nodelist, '$MIGA_TEST_NODELIST')
277
+ end
278
+ ENV['MIGA_TEST_NODELIST'] = f
279
+ capture_stdout { d1.runopts(:nodelist, '$MIGA_TEST_NODELIST') }
280
+ helper_daemon_launch_job
281
+ assert_equal("project1:p:miga-project\n", File.read(t))
282
+ end
283
+
284
+ def test_launch_job_qsub
285
+ $d1.runopts(:type, 'qsub')
286
+ $d1.runopts(:cmd, 'echo {{task_name}}')
287
+ helper_daemon_launch_job
288
+ assert_equal('project1:p:miga-project', $d1.jobs_running.first[:pid])
289
+ end
290
+
291
+ def test_launch_job_failure
292
+ d1 = $d1
293
+ d1.runopts(:type, 'qsub')
294
+ d1.runopts(:cmd, 'echo ""')
295
+ helper_datasets_with_results.first.inactivate!
296
+ capture_stdout { d1.check_project }
297
+ out = capture_stdout { d1.launch_job(d1.jobs_to_run.shift) }
298
+ assert(out.string =~ /Unsuccessful project1:p:miga-project, rescheduling/)
299
+ assert_equal(0, d1.jobs_running.size)
300
+ assert_equal(1, d1.jobs_to_run.size)
301
+ end
302
+
303
+ def helper_daemon_launch_job
304
+ d1 = $d1
305
+ helper_datasets_with_results.first.inactivate!
306
+ assert_equal(0, d1.jobs_to_run.size, 'The queue should be empty')
307
+ capture_stdout { d1.check_project }
308
+ assert_equal(1, d1.jobs_to_run.size, 'The queue should have one job')
309
+ capture_stdout { d1.flush! }
310
+ sleep(1)
311
+ assert_equal(0, d1.jobs_to_run.size, 'There should be nothing running')
312
+ assert_equal(1, d1.jobs_running.size, 'There should be one job running')
313
+ end
314
+
133
315
  end
@@ -0,0 +1,88 @@
1
+ require 'test_helper'
2
+
3
+ class FormatTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ $tmp = Dir.mktmpdir
7
+ end
8
+
9
+ def teardown
10
+ FileUtils.rm_rf $tmp
11
+ end
12
+
13
+ def helper_write_file(content)
14
+ f = File.join($tmp, 'f.fa')
15
+ File.open(f, 'w') { |h| h.print content }
16
+ f
17
+ end
18
+
19
+ def test_clean_fasta
20
+ c = ">MyFa|not-good\nACTG123ACTG-XXNN*\n>GoodOne + spaces!\n\nA C T G .\n"
21
+ cc = ">MyFa|not_good\nACTGACTG-XXNN\n>GoodOne + spaces!\nACTG.\n"
22
+ f = helper_write_file(c)
23
+ assert_equal(c, File.read(f))
24
+ MiGA::MiGA.clean_fasta_file(f)
25
+ assert_equal(cc, File.read(f))
26
+ end
27
+
28
+ def test_wrap_fasta
29
+ c = ">MyFa\n" + ('X' * 150)
30
+ cc = ">MyFa\n" + ('X' * 80) + "\n" + ('X' * 70) + "\n"
31
+ f = helper_write_file(c)
32
+ assert_equal(c, File.read(f))
33
+ MiGA::MiGA.clean_fasta_file(f)
34
+ assert_equal(cc, File.read(f))
35
+ end
36
+
37
+ def test_gz_fasta
38
+ c = ">a-\na"
39
+ cc = ">a_\na\n"
40
+ f = helper_write_file(c)
41
+ assert_equal(c, File.read(f))
42
+ `gzip "#{f}"`
43
+ MiGA::MiGA.clean_fasta_file("#{f}.gz")
44
+ `gzip -d "#{f}.gz"`
45
+ assert_equal(cc, File.read(f))
46
+ end
47
+
48
+ def test_seqs_length_fasta
49
+ c = ">a\nACA\n>b\nACG\n>c\nACTGA\n>d\nGTGAG\n"
50
+ f = helper_write_file(c)
51
+ o = MiGA::MiGA.seqs_length(f, :fasta)
52
+ assert_equal(4.0, o[:avg])
53
+ assert_equal(1.0, o[:var])
54
+ assert_equal(1.0, o[:sd])
55
+ assert_nil(o[:gc])
56
+ assert_nil(o[:n50])
57
+ assert_nil(o[:med])
58
+ o = MiGA::MiGA.seqs_length(f, :fasta, gc: true)
59
+ assert_equal(50.0, o[:gc])
60
+ assert_nil(o[:n50])
61
+ o = MiGA::MiGA.seqs_length(f, :fasta, n50: true)
62
+ assert_nil(o[:gc])
63
+ assert_equal(5, o[:n50])
64
+ o = MiGA::MiGA.seqs_length(f, :fasta, gc: true, n50: true)
65
+ assert_equal(50.0, o[:gc])
66
+ assert_equal(5, o[:n50])
67
+ assert_equal(4.0, o[:med])
68
+ end
69
+
70
+ def test_seqs_length_fastq
71
+ c = "@a\nac\n+\n!!\n@b\ntggg\n+\n####\n@c\nngt\n+\n!!!\n"
72
+ f = helper_write_file(c)
73
+ o = MiGA::MiGA.seqs_length(f, :fastq)
74
+ assert_equal(3.0, o[:avg])
75
+ assert_nil(o[:med])
76
+ o = MiGA::MiGA.seqs_length(f, :fastq, n50: true)
77
+ assert_equal(3, o[:med])
78
+ end
79
+
80
+ def test_tabulate
81
+ tab = MiGA::MiGA.tabulate(%w[a b], [%w[123 45], %w[678 90]])
82
+ assert_equal(' a b ', tab[0])
83
+ assert_equal(' - - ', tab[1])
84
+ assert_equal('123 45', tab[2])
85
+ assert_equal('678 90', tab[3])
86
+ end
87
+
88
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miga-base
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1.0
4
+ version: 0.6.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luis M. Rodriguez-R
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-10 00:00:00.000000000 Z
11
+ date: 2020-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: daemons
@@ -193,6 +193,7 @@ files:
193
193
  - test/common_test.rb
194
194
  - test/daemon_test.rb
195
195
  - test/dataset_test.rb
196
+ - test/format_test.rb
196
197
  - test/hook_test.rb
197
198
  - test/json_test.rb
198
199
  - test/metadata_test.rb