miga-base 0.5.10.0 → 0.6.0.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: a3fc33951911397d8c6305f0bf893cbc373c7ca09f55531c95adf15439e9a938
4
- data.tar.gz: e1fd8f3d96c50770d2dc029fd9285ff79e536df888097d89705087d3a2c9a5d0
3
+ metadata.gz: 13b2ec821a69376491a0a3a31da7ccf717d8aafd1dc7753acaec36f3b7aa42c4
4
+ data.tar.gz: f6d513971e2c06c3f299bc5bf680682c533b380ff4340443f789446a6835b54f
5
5
  SHA512:
6
- metadata.gz: be1241558fc879d2c6e5e6c2cb36432c004b7b4264116d3a2ff9ddc1cf5354efdfe001362a23145a6cc341ee9133d00e181184624a6adf4e40199e0d22b6ffb9
7
- data.tar.gz: f2f14e380a596510f7d2803cac7c41069bbe0a9e2b5d1fd565d57daebbaebfdcd9bf71d1065fb92dfca63e278ff9647cc032abeaea3302ef79c790f481b90bfd
6
+ metadata.gz: 5a4de9af15f63372d98b64cf1604e64fc576c3809f435dc543acb98cab9779155998e5f1b382af856e5d4760faba0d9f606f614654afdad10659b764c9c6a17f
7
+ data.tar.gz: bd01962ae2c1f75ec3768d2515bab711191c4a2b256996c51a69bcbdce5f87b06215816a6368fad0e399d69c09ec7992f97b9868e8670f3866bc1fdcb29d6c41
data/Rakefile CHANGED
@@ -1,18 +1,28 @@
1
- require "rake/testtask"
1
+ require 'rake/testtask'
2
2
 
3
- SOURCES = FileList["lib/**/*.rb"]
3
+ SOURCES = FileList['lib/**/*.rb']
4
4
 
5
- desc "Default Task"
6
- task :default => "test:all"
5
+ desc 'Default Task'
6
+ task :default => 'test:all'
7
7
 
8
- desc "Default tests"
9
- task :test => "test:all"
8
+ desc 'Default tests'
9
+ task :test => 'test:all'
10
10
 
11
11
  namespace :test do
12
- desc "All tests"
12
+ desc 'All tests'
13
13
  Rake::TestTask.new(:all) do |t|
14
- t.libs << "test"
15
- t.pattern = "test/*_test.rb"
14
+ t.libs << 'test'
15
+ t.pattern = 'test/*_test.rb'
16
16
  t.verbose = true
17
17
  end
18
+
19
+ FileList['test/*_test.rb'].each do |i|
20
+ b = File.basename(i, '_test.rb')
21
+ desc "Test #{b}"
22
+ Rake::TestTask.new(:"#{b}") do |t|
23
+ t.libs << 'test'
24
+ t.pattern = i
25
+ t.verbose = true
26
+ end
27
+ end
18
28
  end
@@ -31,33 +31,42 @@ class MiGA::Cli::Action::Run < MiGA::Cli::Action
31
31
  end
32
32
 
33
33
  def perform
34
+ # Load environment variables if requested (typically by the daemon)
34
35
  if cli[:env]
35
36
  cli[:project] ||= ENV['PROJECT']
36
37
  cli[:dataset] ||= ENV['DATASET']
37
38
  cli[:thr] ||= ENV['CORES'].to_i unless ENV['CORES'].nil?
38
39
  cli[:result] = File.basename(cli[:result].to_s, '.bash').to_sym
39
40
  end
40
- virtual_task = false
41
- miga = MiGA.root_path
41
+
42
+ # Unset dataset if the requested result is for projects
43
+ if (MiGA::Project.RESULT_DIRS.keys + [:p]).include? cli[:result]
44
+ cli[:dataset] = nil
45
+ end
46
+
47
+ # Load project
42
48
  p = cli.load_project
43
49
 
50
+ # Prepare command
51
+ miga = MiGA.root_path
44
52
  cmd = ["PROJECT=#{p.path.shellescape}",
45
53
  "RUNTYPE=#{cli[:remote] ? 'ssh' : 'bash'}",
46
54
  "MIGA=#{miga.shellescape}", "CORES=#{cli[:thr]}"]
47
55
  obj = cli.load_project_or_dataset
48
56
  klass = obj.class
49
- virtual_task = true if [:p, :d].include? cli[:result]
57
+ virtual_task = [:p, :d].include?(cli[:result])
50
58
  cmd << "DATASET=#{obj.name.shellescape}" if obj.is_a? MiGA::Dataset
51
59
  if klass.RESULT_DIRS[cli[:result]].nil? and not virtual_task
52
60
  raise "Unsupported #{klass.to_s.sub(/.*::/, '')} result: #{cli[:result]}."
53
61
  end
54
62
  cmd << MiGA.script_path(cli[:result], miga: miga, project: p).shellescape
55
63
  if cli[:remote]
56
- #cmd.unshift '.', '/etc/profile', ';'
57
64
  cmd = ['ssh', '-t', '-t', cli[:remote].shellescape,
58
65
  cmd.join(' ').shellescape]
59
66
  end
60
67
  cmd << ['>', cli[:log].shellescape, '2>&1'] if cli[:log]
68
+
69
+ # Launch
61
70
  pid = spawn cmd.join(' ')
62
71
  Process.wait pid
63
72
  end
@@ -30,12 +30,7 @@ class MiGA::Cli::Action::Stats < MiGA::Cli::Action
30
30
  r = cli.load_result
31
31
  if cli[:compute]
32
32
  cli.say 'Computing statistics'
33
- method = :"compute_#{cli[:result]}"
34
- stats = self.respond_to?(method, true) ? send(method, r) : nil
35
- unless stats.nil?
36
- r[:stats] = stats
37
- r.save
38
- end
33
+ r.compute_stats
39
34
  end
40
35
  if cli[:key].nil?
41
36
  r[:stats].each do |k, v|
@@ -47,143 +42,4 @@ class MiGA::Cli::Action::Stats < MiGA::Cli::Action
47
42
  puts v.is_a?(Array) ? v.first : v
48
43
  end
49
44
  end
50
-
51
- private
52
-
53
- def compute_raw_reads(r)
54
- stats = {}
55
- if r[:files][:pair1].nil?
56
- s = MiGA.seqs_length(r.file_path(:single), :fastq, gc: true)
57
- stats = {
58
- reads: s[:n],
59
- length_average: [s[:avg], 'bp'],
60
- length_standard_deviation: [s[:sd], 'bp'],
61
- g_c_content: [s[:gc], '%']}
62
- else
63
- s1 = MiGA.seqs_length(r.file_path(:pair1), :fastq, gc: true)
64
- s2 = MiGA.seqs_length(r.file_path(:pair2), :fastq, gc: true)
65
- stats = {
66
- read_pairs: s1[:n],
67
- forward_length_average: [s1[:avg], 'bp'],
68
- forward_length_standard_deviation: [s1[:sd], 'bp'],
69
- forward_g_c_content: [s1[:gc], '%'],
70
- reverse_length_average: [s2[:avg], 'bp'],
71
- reverse_length_standard_deviation: [s2[:sd], 'bp'],
72
- reverse_g_c_content: [s2[:gc], '%']}
73
- end
74
- stats
75
- end
76
-
77
- def compute_trimmed_fasta(r)
78
- f = r[:files][:coupled].nil? ? r.file_path(:single) : r.file_path(:coupled)
79
- s = MiGA.seqs_length(f, :fasta, gc: true)
80
- {
81
- reads: s[:n],
82
- length_average: [s[:avg], 'bp'],
83
- length_standard_deviation: [s[:sd], 'bp'],
84
- g_c_content: [s[:gc], '%']
85
- }
86
- end
87
-
88
- def compute_assembly(r)
89
- s = MiGA.seqs_length(r.file_path(:largecontigs), :fasta,
90
- n50: true, gc: true)
91
- {
92
- contigs: s[:n],
93
- n50: [s[:n50], 'bp'],
94
- total_length: [s[:tot], 'bp'],
95
- g_c_content: [s[:gc], '%']
96
- }
97
- end
98
-
99
- def compute_cds(r)
100
- s = MiGA.seqs_length(r.file_path(:proteins), :fasta)
101
- stats = {
102
- predicted_proteins: s[:n],
103
- average_length: [s[:avg], 'aa']}
104
- asm = cli.load_dataset.add_result(:assembly, false)
105
- unless asm.nil? or asm[:stats][:total_length].nil?
106
- stats[:coding_density] =
107
- [300.0 * s[:tot] / asm[:stats][:total_length][0], '%']
108
- end
109
- stats
110
- end
111
-
112
- def compute_essential_genes(r)
113
- stats = {}
114
- d = cli.load_dataset
115
- if d.is_multi?
116
- stats = {median_copies: 0, mean_copies: 0}
117
- File.open(r.file_path(:report), 'r') do |fh|
118
- fh.each_line do |ln|
119
- if /^! (Mean|Median) number of copies per model: (.*)\./.match(ln)
120
- stats["#{$1.downcase}_copies".to_sym] = $2.to_f
121
- end
122
- end
123
- end
124
- else
125
- # Fix estimate by domain
126
- if !(tax = d.metadata[:tax]).nil? &&
127
- %w[Archaea Bacteria].include?(tax[:d]) &&
128
- r.file_path(:raw_report).nil?
129
- scr = "#{MiGA.root_path}/utils/domain-ess-genes.rb"
130
- rep = r.file_path(:report)
131
- rc_p = File.expand_path('.miga_rc', ENV['HOME'])
132
- rc = File.exist?(rc_p) ? ". '#{rc_p}' && " : ''
133
- $stderr.print `#{rc} ruby '#{scr}' \
134
- '#{rep}' '#{rep}.domain' '#{tax[:d][0]}'`
135
- r.add_file(:raw_report, "#{d.name}.ess/log")
136
- r.add_file(:report, "#{d.name}.ess/log.domain")
137
- end
138
- # Extract/compute quality values
139
- stats = {completeness: [0.0, '%'], contamination: [0.0, '%']}
140
- File.open(r.file_path(:report), 'r') do |fh|
141
- fh.each_line do |ln|
142
- if /^! (Completeness|Contamination): (.*)%/.match(ln)
143
- stats[$1.downcase.to_sym][0] = $2.to_f
144
- end
145
- end
146
- end
147
- stats[:quality] = stats[:completeness][0] - stats[:contamination][0] * 5
148
- d.metadata[:quality] = case stats[:quality]
149
- when 80..100 ; :excellent
150
- when 50..80 ; :high
151
- when 20..50 ; :intermediate
152
- else ; :low
153
- end
154
- d.save
155
- end
156
- stats
157
- end
158
-
159
- def compute_ssu(r)
160
- stats = {ssu: 0, complete_ssu: 0}
161
- Zlib::GzipReader.open(r.file_path(:gff)) do |fh|
162
- fh.each_line do |ln|
163
- next if ln =~ /^#/
164
- rl = ln.chomp.split("\t")
165
- len = (rl[4].to_i - rl[3].to_i).abs + 1
166
- stats[:max_length] = [stats[:max_length] || 0, len].max
167
- stats[:ssu] += 1
168
- stats[:complete_ssu] += 1 unless rl[8] =~ /\(partial\)/
169
- end
170
- end
171
- stats
172
- end
173
-
174
- def compute_taxonomy(r)
175
- stats = {}
176
- File.open(r.file_path(:intax_test), 'r') do |fh|
177
- fh.gets.chomp =~ /Closest relative: (\S+) with AAI: (\S+)\.?/
178
- stats[:closest_relative] = $1
179
- stats[:aai] = [$2.to_f, '%']
180
- 3.times { fh.gets }
181
- fh.each_line do |ln|
182
- row = ln.chomp.gsub(/^\s*/,'').split(/\s+/)
183
- break if row.empty?
184
- stats[:"#{row[0]}_pvalue"] = row[2].to_f unless row[0] == 'root'
185
- end
186
- end
187
- stats
188
- end
189
45
  end
data/lib/miga/cli/base.rb CHANGED
@@ -14,7 +14,6 @@ module MiGA::Cli::Base
14
14
  # Projects
15
15
  new: 'Creates an empty MiGA project',
16
16
  about: 'Displays information about a MiGA project',
17
- plugins: 'Lists or (un)installs plugins in a MiGA project',
18
17
  doctor: 'Performs consistency checks on a MiGA project',
19
18
  get_db: 'Downloads a pre-indexed database',
20
19
  # Datasets
@@ -0,0 +1,49 @@
1
+
2
+ ##
3
+ # Helper module including specific functions to handle dataset hooks.
4
+ module MiGA::Common::Hooks
5
+
6
+ ##
7
+ # Call the hook with symbol +event+ and any parameters +event_args+
8
+ def pull_hook(event, *event_args)
9
+ event = event.to_sym
10
+ event_queue = (hooks[event] || [])
11
+ event_queue += (metadata[event] || []) if respond_to? :metadata
12
+ event_queue.each do |i|
13
+ action = i.first
14
+ hook_name = :"hook_#{action}"
15
+ hook_args = i[1 .. -1]
16
+ if respond_to? hook_name
17
+ MiGA::MiGA.DEBUG "Hook: #{self.class}(#{event} > #{action})"
18
+ self.send(hook_name, hook_args, event_args)
19
+ else
20
+ raise "Cannot find action #{action} elicited by #{self.class}(#{event})"
21
+ end
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Whenever +event+ occurs, launch +action+ with parameters +args+.
27
+ def add_hook(event, action, *args)
28
+ (hooks[event] ||= []) << [action, *args]
29
+ end
30
+
31
+ ##
32
+ # Get the stack of hooks
33
+ def hooks
34
+ @_hooks ||= default_hooks
35
+ end
36
+
37
+ ##
38
+ # Default object's hooks
39
+ def default_hooks
40
+ {}
41
+ end
42
+
43
+ ##
44
+ # Run the function defined in the first hook argument
45
+ def hook_run_lambda(hook_args, event_args)
46
+ hook_args.first[*event_args]
47
+ end
48
+
49
+ end
@@ -11,17 +11,8 @@ module MiGA::Common::Path
11
11
  # Path to a script to be executed for +task+. Supported +opts+ are:
12
12
  # - +:miga+ Path to the MiGA home to use. If not passed, the home of the
13
13
  # library is used).
14
- # - +:project+ MiGA::Project object to check within plugins. If not passed,
15
- # only core scripts are supported.
16
14
  def script_path(task, opts={})
17
15
  opts[:miga] ||= root_path
18
- unless opts[:project].nil?
19
- opts[:project].plugins.each do |pl|
20
- if File.exist? File.expand_path("scripts/#{task}.bash", pl)
21
- opts[:miga] = pl
22
- end
23
- end
24
- end
25
16
  File.expand_path("scripts/#{task}.bash", opts[:miga])
26
17
  end
27
18
 
data/lib/miga/daemon.rb CHANGED
@@ -146,8 +146,7 @@ class MiGA::Daemon < MiGA::MiGA
146
146
  def check_project
147
147
  return if project.dataset_names.empty?
148
148
  return unless project.done_preprocessing?(false)
149
- to_run = project.next_distances(true)
150
- to_run = project.next_inclade(true) if to_run.nil?
149
+ to_run = project.next_task(nil, false)
151
150
  queue_job(:p) unless to_run.nil?
152
151
  end
153
152
 
data/lib/miga/dataset.rb CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'miga/metadata'
5
5
  require 'miga/dataset/result'
6
+ require 'miga/dataset/hooks'
6
7
  require 'sqlite3'
7
8
 
8
9
  ##
@@ -10,6 +11,7 @@ require 'sqlite3'
10
11
  class MiGA::Dataset < MiGA::MiGA
11
12
 
12
13
  include MiGA::Dataset::Result
14
+ include MiGA::Dataset::Hooks
13
15
 
14
16
  # Class-level
15
17
  class << self
@@ -56,6 +58,7 @@ class MiGA::Dataset < MiGA::MiGA
56
58
  metadata
57
59
  ]
58
60
  save unless File.exist? @metadata_future[0]
61
+ pull_hook :on_load
59
62
  end
60
63
 
61
64
  ##
@@ -69,6 +72,7 @@ class MiGA::Dataset < MiGA::MiGA
69
72
  def save
70
73
  MiGA.DEBUG "Dataset.metadata: #{metadata.data}"
71
74
  metadata.save
75
+ pull_hook :on_save
72
76
  end
73
77
 
74
78
  ##
@@ -81,6 +85,7 @@ class MiGA::Dataset < MiGA::MiGA
81
85
  def remove!
82
86
  self.results.each{ |r| r.remove! }
83
87
  self.metadata.remove!
88
+ pull_hook :on_remove
84
89
  end
85
90
 
86
91
  ##
@@ -88,6 +93,7 @@ class MiGA::Dataset < MiGA::MiGA
88
93
  def inactivate!
89
94
  self.metadata[:inactive] = true
90
95
  self.metadata.save
96
+ pull_hook :on_inactivate
91
97
  end
92
98
 
93
99
  ##
@@ -95,6 +101,7 @@ class MiGA::Dataset < MiGA::MiGA
95
101
  def activate!
96
102
  self.metadata[:inactive] = nil
97
103
  self.metadata.save
104
+ pull_hook :on_activate
98
105
  end
99
106
 
100
107
  ##
@@ -0,0 +1,72 @@
1
+
2
+ require 'miga/common/hooks'
3
+
4
+ ##
5
+ # Helper module including specific functions to handle dataset hooks.
6
+ # Supported events:
7
+ # - on_load(): When loaded
8
+ # - on_save(): When saved
9
+ # - on_remove(): When removed
10
+ # - on_inactivate(): When inactivated
11
+ # - on_activate(): When activated
12
+ # - on_result_ready(result): When any result is ready, with key +result+
13
+ # - on_result_ready_{result}(): When +result+ is ready
14
+ # - on_preprocessing_ready(): When preprocessing is complete
15
+ # Supported hooks:
16
+ # - run_lambda(lambda, args...)
17
+ # - clear_run_counts()
18
+ # - run_cmd(cmd)
19
+ # Internal hooks:
20
+ # - _pull_preprocessing_ready_hooks()
21
+ # - _pull_result_hooks()
22
+ module MiGA::Dataset::Hooks
23
+
24
+ include MiGA::Common::Hooks
25
+
26
+ def default_hooks
27
+ {
28
+ on_preprocessing_ready: [[:clear_run_counts]],
29
+ on_result_ready: [
30
+ [:_pull_result_hooks],
31
+ [:_pull_preprocessing_ready_hooks]
32
+ ]
33
+ }
34
+ end
35
+
36
+ ##
37
+ # Clear metadata from run counts
38
+ def hook_clear_run_counts(_hook_args, _event_args)
39
+ metadata.data.keys
40
+ .select { |k| k.to_s =~ /^_try_/ }
41
+ .each { |k| metadata[k] = nil }
42
+ save
43
+ end
44
+
45
+ ##
46
+ # Run +cmd+ in the command-line with {{variables}}: dataset, project, miga,
47
+ # object (as defined for the event, if any)
48
+ # - +hook_args+: +[cmd]+
49
+ # - +event_args+: +[object (optional)]+
50
+ def hook_run_cmd(hook_args, event_args)
51
+ Process.wait(
52
+ spawn hook_args.first.miga_variables(
53
+ dataset: name, project: project.path, miga: MiGA::MiGA.root_path,
54
+ object: event_args.first
55
+ )
56
+ )
57
+ end
58
+
59
+ ##
60
+ # Pull :dataset_ready hook if preprocessing is complete
61
+ def hook__pull_preprocessing_ready_hooks(_hook_args, _event_args)
62
+ pull_hook(:on_preprocessing_ready) if done_preprocessing?
63
+ end
64
+
65
+ ##
66
+ # Dataset Action :pull_result_hooks([], [res])
67
+ # Pull the hook specific to the type of result
68
+ def hook__pull_result_hooks(_hook_args, event_args)
69
+ pull_hook(:"on_result_ready_#{event_args.first}", *event_args)
70
+ end
71
+
72
+ end