miga-base 0.5.10.0 → 0.6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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