miga-base 0.4.3.0 → 0.5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/miga/cli.rb +43 -223
  4. data/lib/miga/cli/action/add.rb +91 -62
  5. data/lib/miga/cli/action/classify_wf.rb +97 -0
  6. data/lib/miga/cli/action/daemon.rb +14 -10
  7. data/lib/miga/cli/action/derep_wf.rb +95 -0
  8. data/lib/miga/cli/action/doctor.rb +83 -55
  9. data/lib/miga/cli/action/get.rb +68 -52
  10. data/lib/miga/cli/action/get_db.rb +206 -0
  11. data/lib/miga/cli/action/index_wf.rb +31 -0
  12. data/lib/miga/cli/action/init.rb +115 -190
  13. data/lib/miga/cli/action/init/daemon_helper.rb +124 -0
  14. data/lib/miga/cli/action/ls.rb +20 -11
  15. data/lib/miga/cli/action/ncbi_get.rb +199 -157
  16. data/lib/miga/cli/action/preproc_wf.rb +46 -0
  17. data/lib/miga/cli/action/quality_wf.rb +45 -0
  18. data/lib/miga/cli/action/stats.rb +147 -99
  19. data/lib/miga/cli/action/summary.rb +10 -4
  20. data/lib/miga/cli/action/tax_dist.rb +61 -46
  21. data/lib/miga/cli/action/tax_test.rb +46 -39
  22. data/lib/miga/cli/action/wf.rb +178 -0
  23. data/lib/miga/cli/base.rb +11 -0
  24. data/lib/miga/cli/objects_helper.rb +88 -0
  25. data/lib/miga/cli/opt_helper.rb +160 -0
  26. data/lib/miga/daemon.rb +7 -4
  27. data/lib/miga/dataset/base.rb +5 -5
  28. data/lib/miga/project/base.rb +4 -4
  29. data/lib/miga/project/result.rb +2 -1
  30. data/lib/miga/remote_dataset/base.rb +5 -5
  31. data/lib/miga/remote_dataset/download.rb +1 -1
  32. data/lib/miga/version.rb +3 -3
  33. data/scripts/cds.bash +3 -1
  34. data/scripts/essential_genes.bash +1 -0
  35. data/scripts/stats.bash +1 -1
  36. data/scripts/trimmed_fasta.bash +5 -3
  37. data/utils/distance/runner.rb +3 -0
  38. data/utils/distance/temporal.rb +10 -1
  39. data/utils/enveomics/Manifest/Tasks/fasta.json +5 -0
  40. data/utils/enveomics/Manifest/Tasks/sequence-identity.json +7 -0
  41. data/utils/enveomics/Scripts/BlastTab.addlen.rb +33 -31
  42. data/utils/enveomics/Scripts/FastA.tag.rb +42 -41
  43. data/utils/enveomics/Scripts/HMM.essential.rb +85 -55
  44. data/utils/enveomics/Scripts/HMM.haai.rb +29 -20
  45. data/utils/enveomics/Scripts/SRA.download.bash +1 -1
  46. data/utils/enveomics/Scripts/aai.rb +163 -128
  47. data/utils/enveomics/build_enveomics_r.bash +11 -10
  48. data/utils/enveomics/enveomics.R/DESCRIPTION +3 -2
  49. data/utils/enveomics/enveomics.R/R/autoprune.R +141 -107
  50. data/utils/enveomics/enveomics.R/R/barplot.R +105 -86
  51. data/utils/enveomics/enveomics.R/R/cliopts.R +131 -115
  52. data/utils/enveomics/enveomics.R/R/df2dist.R +144 -106
  53. data/utils/enveomics/enveomics.R/R/growthcurve.R +201 -133
  54. data/utils/enveomics/enveomics.R/R/recplot.R +350 -315
  55. data/utils/enveomics/enveomics.R/R/recplot2.R +1334 -914
  56. data/utils/enveomics/enveomics.R/R/tribs.R +521 -361
  57. data/utils/enveomics/enveomics.R/R/utils.R +31 -15
  58. data/utils/enveomics/enveomics.R/README.md +7 -0
  59. data/utils/enveomics/enveomics.R/man/cash-enve.GrowthCurve-method.Rd +17 -0
  60. data/utils/enveomics/enveomics.R/man/cash-enve.RecPlot2-method.Rd +17 -0
  61. data/utils/enveomics/enveomics.R/man/cash-enve.RecPlot2.Peak-method.Rd +17 -0
  62. data/utils/enveomics/enveomics.R/man/enve.GrowthCurve-class.Rd +16 -21
  63. data/utils/enveomics/enveomics.R/man/enve.TRIBS-class.Rd +31 -28
  64. data/utils/enveomics/enveomics.R/man/enve.TRIBS.merge.Rd +23 -19
  65. data/utils/enveomics/enveomics.R/man/enve.TRIBStest-class.Rd +36 -26
  66. data/utils/enveomics/enveomics.R/man/enve.__prune.iter.Rd +23 -24
  67. data/utils/enveomics/enveomics.R/man/enve.__prune.reduce.Rd +23 -24
  68. data/utils/enveomics/enveomics.R/man/enve.__tribs.Rd +32 -33
  69. data/utils/enveomics/enveomics.R/man/enve.barplot.Rd +91 -64
  70. data/utils/enveomics/enveomics.R/man/enve.cliopts.Rd +57 -37
  71. data/utils/enveomics/enveomics.R/man/enve.col.alpha.Rd +24 -19
  72. data/utils/enveomics/enveomics.R/man/enve.col2alpha.Rd +19 -18
  73. data/utils/enveomics/enveomics.R/man/enve.df2dist.Rd +39 -26
  74. data/utils/enveomics/enveomics.R/man/enve.df2dist.group.Rd +38 -25
  75. data/utils/enveomics/enveomics.R/man/enve.df2dist.list.Rd +40 -26
  76. data/utils/enveomics/enveomics.R/man/enve.growthcurve.Rd +67 -49
  77. data/utils/enveomics/enveomics.R/man/enve.prune.dist.Rd +37 -28
  78. data/utils/enveomics/enveomics.R/man/enve.recplot.Rd +122 -97
  79. data/utils/enveomics/enveomics.R/man/enve.recplot2-class.Rd +35 -31
  80. data/utils/enveomics/enveomics.R/man/enve.recplot2.ANIr.Rd +24 -23
  81. data/utils/enveomics/enveomics.R/man/enve.recplot2.Rd +68 -51
  82. data/utils/enveomics/enveomics.R/man/enve.recplot2.__counts.Rd +25 -24
  83. data/utils/enveomics/enveomics.R/man/enve.recplot2.__peakHist.Rd +21 -22
  84. data/utils/enveomics/enveomics.R/man/enve.recplot2.__whichClosestPeak.Rd +19 -20
  85. data/utils/enveomics/enveomics.R/man/enve.recplot2.changeCutoff.Rd +19 -18
  86. data/utils/enveomics/enveomics.R/man/enve.recplot2.compareIdentities.Rd +41 -32
  87. data/utils/enveomics/enveomics.R/man/enve.recplot2.coordinates.Rd +29 -24
  88. data/utils/enveomics/enveomics.R/man/enve.recplot2.corePeak.Rd +18 -18
  89. data/utils/enveomics/enveomics.R/man/enve.recplot2.extractWindows.Rd +40 -34
  90. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.Rd +36 -24
  91. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.__em_e.Rd +19 -20
  92. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.__em_m.Rd +19 -20
  93. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.__emauto_one.Rd +27 -29
  94. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.__mow_one.Rd +41 -42
  95. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.__mower.Rd +17 -18
  96. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.em.Rd +43 -33
  97. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.emauto.Rd +36 -28
  98. data/utils/enveomics/enveomics.R/man/enve.recplot2.findPeaks.mower.Rd +74 -56
  99. data/utils/enveomics/enveomics.R/man/enve.recplot2.peak-class.Rd +44 -31
  100. data/utils/enveomics/enveomics.R/man/enve.recplot2.seqdepth.Rd +27 -22
  101. data/utils/enveomics/enveomics.R/man/enve.recplot2.windowDepthThreshold.Rd +32 -26
  102. data/utils/enveomics/enveomics.R/man/enve.tribs.Rd +59 -44
  103. data/utils/enveomics/enveomics.R/man/enve.tribs.test.Rd +28 -21
  104. data/utils/enveomics/enveomics.R/man/enve.truncate.Rd +27 -22
  105. data/utils/enveomics/enveomics.R/man/plot.enve.GrowthCurve.Rd +63 -43
  106. data/utils/enveomics/enveomics.R/man/plot.enve.TRIBS.Rd +38 -29
  107. data/utils/enveomics/enveomics.R/man/plot.enve.TRIBStest.Rd +38 -30
  108. data/utils/enveomics/enveomics.R/man/plot.enve.recplot2.Rd +111 -83
  109. data/utils/enveomics/enveomics.R/man/summary.enve.GrowthCurve.Rd +19 -18
  110. data/utils/enveomics/enveomics.R/man/summary.enve.TRIBS.Rd +19 -18
  111. data/utils/enveomics/enveomics.R/man/summary.enve.TRIBStest.Rd +19 -18
  112. data/utils/find-medoid.R +3 -2
  113. data/utils/representatives.rb +5 -3
  114. data/utils/subclade/pipeline.rb +22 -11
  115. data/utils/subclade/runner.rb +5 -1
  116. data/utils/subclades-compile.rb +1 -1
  117. data/utils/subclades.R +9 -3
  118. metadata +15 -4
  119. data/utils/enveomics/enveomics.R/man/enveomics.R-package.Rd +0 -15
  120. data/utils/enveomics/enveomics.R/man/z$-methods.Rd +0 -26
@@ -29,49 +29,56 @@ class MiGA::Cli::Action::TaxTest < MiGA::Cli::Action
29
29
  raise 'Action not supported for the project or dataset' if cr.nil?
30
30
  raise 'No close relatives found'
31
31
  else
32
- cli.say 'Querying probability distributions'
33
- cr = cr[0]
34
- cli.puts "Closest relative: #{cr[0]} with AAI: #{cr[1]}."
35
- if self[:ref_project]
36
- if (ref = p.metadata[:ref_project]).nil?
37
- raise '--ref-project requested but no reference project has been set'
38
- end
39
- if (q = MiGA::Project.load(ref)).nil?
40
- raise '--ref-project requested but reference project doesn\'t exist'
41
- end
42
- cr_d = q.dataset(cr[0])
43
- else
44
- cr_d = p.dataset(cr[0])
45
- end
46
- tax = cr_d.metadata[:tax] unless cr_d.nil?
47
- tax ||= {}
32
+ query_probability_distributions(d, cr[0])
33
+ end
34
+ end
48
35
 
49
- if %w[intax both].include? cli[:test]
50
- # Intax
51
- r = TaxDist.aai_pvalues(cr[1], :intax).map do |k,v|
52
- sig = ''
53
- [0.5, 0.1, 0.05, 0.01].each{ |i| sig << '*' if v<i }
54
- [Taxonomy.LONG_RANKS[k], (tax[k] || '?'), v, sig]
55
- end
56
- cli.puts ''
57
- cli.puts 'Taxonomic classification'
58
- cli.puts MiGA.tabulate(%w[Rank Taxonomy P-value Signif.], r)
59
- end
36
+ private
60
37
 
61
- if %w[novel both].include? cli[:test]
62
- # Novel
63
- r = TaxDist.aai_pvalues(cr[1], :novel).map do |k,v|
64
- sig = ''
65
- [0.5, 0.1, 0.05, 0.01].each{ |i| sig << '*' if v<i }
66
- [Taxonomy.LONG_RANKS[k], v, sig]
67
- end
68
- cli.puts ''
69
- cli.puts 'Taxonomic novelty'
70
- cli.puts MiGA.tabulate(%w[Rank P-value Signif.], r)
38
+ def query_probability_distributions(d, cr)
39
+ cli.say 'Querying probability distributions'
40
+ tax = closest_relative_tax(cr)
41
+ if %w[intax both].include? cli[:test]
42
+ r = test_closest_relative(cr, tax, :intax)
43
+ cli.puts ''
44
+ cli.puts 'Taxonomic classification'
45
+ cli.puts MiGA.tabulate(%w[Rank Taxonomy P-value Signif.], r)
46
+ end
47
+ if %w[novel both].include? cli[:test]
48
+ r = test_closest_relative(cr, tax, :novel)
49
+ r.map! { |i| i.tap { |j| j.delete_at(1) } }
50
+ cli.puts ''
51
+ cli.puts 'Taxonomic novelty'
52
+ cli.puts MiGA.tabulate(%w[Rank P-value Signif.], r)
53
+ end
54
+ cli.puts ''
55
+ cli.puts 'Significance at p-value below: *0.5, **0.1, ***0.05, ****0.01.'
56
+ end
57
+
58
+ def closest_relative_tax(cr)
59
+ cli.puts "Closest relative: #{cr[0]} with AAI: #{cr[1]}."
60
+ p = cli.load_project
61
+ if cli[:ref_project]
62
+ if (ref = p.metadata[:ref_project]).nil?
63
+ raise '--ref-project requested but no reference project has been set'
71
64
  end
65
+ if (q = MiGA::Project.load(ref)).nil?
66
+ raise '--ref-project requested but reference project doesn\'t exist'
67
+ end
68
+ cr_d = q.dataset(cr[0])
69
+ else
70
+ cr_d = p.dataset(cr[0])
71
+ end
72
+ tax = cr_d.metadata[:tax] unless cr_d.nil?
73
+ tax ||= {}
74
+ tax
75
+ end
72
76
 
73
- cli.puts ''
74
- cli.puts 'Significance at p-value below: *0.5, **0.1, ***0.05, ****0.01.'
77
+ def test_closest_relative(cr, tax, test)
78
+ TaxDist.aai_pvalues(cr[1], test).map do |k,v|
79
+ sig = ''
80
+ [0.5, 0.1, 0.05, 0.01].each { |i| sig << '*' if v < i }
81
+ [Taxonomy.LONG_RANKS[k], (tax[k] || '?'), v, sig]
75
82
  end
76
83
  end
77
84
  end
@@ -0,0 +1,178 @@
1
+ # @package MiGA
2
+ # @license Artistic-2.0
3
+
4
+ ##
5
+ # Helper module for workflows
6
+ module MiGA::Cli::Action::Wf
7
+ def default_opts_for_wf
8
+ cli.expect_files = true
9
+ cli.defaults = {
10
+ clean: false, regexp: MiGA::Cli.FILE_REGEXP,
11
+ project_type: :genomes, dataset_type: :popgenome,
12
+ ncbi_draft: true }
13
+ end
14
+
15
+ def opts_for_wf(opt, files_desc, params = {})
16
+ {
17
+ multi: false, cleanup: true, project_type: false, ncbi: true
18
+ }.each { |k, v| params[k] = v if params[k].nil? }
19
+ opt.on(
20
+ '-o', '--out_dir PATH',
21
+ '(Mandatory) Directory to be created with all output data'
22
+ ) { |v| cli[:outdir] = v }
23
+ opt.separator ''
24
+ opt.separator " FILES...: #{files_desc}"
25
+ opt.separator ''
26
+ opt.separator 'Workflow Control Options'
27
+ if params[:ncbi]
28
+ opt.on(
29
+ '-T', '--ncbi-taxon STRING',
30
+ 'Download all the genomes in NCBI classified as this taxon'
31
+ ) { |v| cli[:ncbi_taxon] = v }
32
+ opt.on(
33
+ '--no-draft',
34
+ 'Only download complete genomes, not drafts'
35
+ ) { |v| cli[:ncbi_draft] = v }
36
+ end
37
+ if params[:cleanup]
38
+ opt.on(
39
+ '-c', '--clean',
40
+ 'Clean all intermediate files after generating the reports'
41
+ ) { |v| cli[:clean] = v }
42
+ end
43
+ opt.on(
44
+ '-R', '--name-regexp REGEXP', Regexp,
45
+ 'Regular expression indicating how to extract the name from the path',
46
+ "By default: '#{cli[:regexp]}'"
47
+ ) { |v| cli[:regexp] = v }
48
+ opt.on(
49
+ '--type STRING',
50
+ "Type of datasets. By default: #{cli[:dataset_type]}",
51
+ 'Recognized types:',
52
+ *MiGA::Dataset.KNOWN_TYPES
53
+ .map do |k, v|
54
+ "~ #{k}: #{v[:description]}" unless !params[:multi] && v[:multi]
55
+ end.compact
56
+ ) { |v| cli[:dataset_type] = v.downcase.to_sym }
57
+ if params[:project_type]
58
+ opt.on(
59
+ '--project-type STRING',
60
+ "Type of project. By default: #{cli[:project_type]}",
61
+ 'Recognized types:',
62
+ *MiGA::Project.KNOWN_TYPES
63
+ .map do |k, v|
64
+ "~ #{k}: #{v[:description]}" unless !params[:multi] && v[:multi]
65
+ end.compact
66
+ ) { |v| cli[:project_type] = v.downcase.to_sym }
67
+ end
68
+ opt.on(
69
+ '--daemon PATH',
70
+ 'Use custom daemon configuration in JSON format',
71
+ 'By default: ~/.miga_daemon.json'
72
+ ) { |v| cli[:daemon_json] = v }
73
+ opt.on(
74
+ '-j', '--jobs INT',
75
+ 'Number of parallel jobs to execute',
76
+ 'By default controlled by the daemon configuration (maxjobs)'
77
+ ) { |v| cli[:jobs] = v.to_i }
78
+ opt.on(
79
+ '-t', '--threads INT',
80
+ 'Number of CPUs to use per job',
81
+ 'By default controlled by the daemon configuration (ppn)'
82
+ ) { |v| cli[:threads] = v.to_i }
83
+ end
84
+
85
+ def opts_for_wf_distances(opt)
86
+ opt.on('--fast', 'Alias to: --aai-p diamond --ani-p fastani') do
87
+ cli[:aai_p] = 'diamond'
88
+ cli[:ani_p] = 'fastani'
89
+ end
90
+ opt.on(
91
+ '--haai-p STRING',
92
+ 'hAAI search engine. One of: blast+ (default), blat, diamond, no'
93
+ ) { |v| cli[:haai_p] = v }
94
+ opt.on(
95
+ '--aai-p STRING',
96
+ 'AAI search engine. One of: blast+ (default), blat, diamond'
97
+ ) { |v| cli[:aai_p] = v }
98
+ opt.on(
99
+ '--ani-p STRING',
100
+ 'ANI search engine. One of: blast+ (default), blat, fastani'
101
+ ) { |v| cli[:ani_p] = v }
102
+ end
103
+
104
+ def create_project(stage, p_metadata = {}, d_metadata = {})
105
+ cli.ensure_par(
106
+ outdir: '-o',
107
+ project_type: '--project-type',
108
+ dataset_type: '--dataset-type')
109
+ # Create empty project
110
+ call_cli([
111
+ 'new',
112
+ '-P', cli[:outdir],
113
+ '-t', cli[:project_type],
114
+ '-m', p_metadata.map{ |k,v| "#{k}=#{v}" }.join(',')
115
+ ])
116
+ # Download datasets
117
+ call_cli([
118
+ 'ncbi_get',
119
+ '-P', cli[:outdir],
120
+ '-T', cli[:ncbi_taxon],
121
+ (cli[:ncbi_draft] ? '--all' : '--complete'),
122
+ '-m', d_metadata.map{ |k,v| "#{k}=#{v}" }.join(',')
123
+ ]) unless cli[:ncbi_taxon].nil?
124
+ # Add datasets
125
+ call_cli([
126
+ 'add',
127
+ '-P', cli[:outdir],
128
+ '-t', cli[:dataset_type],
129
+ '-i', stage,
130
+ '-R', cli[:regexp],
131
+ '-m', d_metadata.map{ |k,v| "#{k}=#{v}" }.join(',')
132
+ ] + cli.files) unless cli.files.empty?
133
+ p = MiGA::Project.load(cli[:outdir])
134
+ raise "Impossible to create project: #{cli[:outdir]}" if p.nil?
135
+ [:haai_p, :aai_p, :ani_p].each do |i|
136
+ p.metadata[i] = cli[i] unless cli[i].nil?
137
+ end
138
+ p.save
139
+ p
140
+ end
141
+
142
+ def summarize(which = %w[cds assembly essential_genes ssu])
143
+ which.each do |r|
144
+ cli.say "Summary: #{r}"
145
+ call_cli([
146
+ 'summary',
147
+ '-P', cli[:outdir],
148
+ '-r', r,
149
+ '-o', File.expand_path("#{r}.tsv", cli[:outdir]),
150
+ '--tab'
151
+ ])
152
+ end
153
+ end
154
+
155
+ def cleanup
156
+ return unless cli[:clean]
157
+ cli.say "Cleaning up intermediate files"
158
+ %w[data daemon metadata miga.project.json].each do |f|
159
+ FileUtils.rm_rf(File.expand_path(f, cli[:outdir]))
160
+ end
161
+ end
162
+
163
+ def call_cli(cmd)
164
+ cmd << '-v' if cli[:verbose]
165
+ MiGA::Cli.new(cmd.map(&:to_s)).launch
166
+ end
167
+
168
+ def run_daemon
169
+ cmd = ['daemon', 'run', '-P', cli[:outdir], '--shutdown-when-done']
170
+ cmd += ['--json', cli[:daemon_json]] unless cli[:daemon_json].nil?
171
+ cmd += ['--max-jobs', cli[:jobs]] unless cli[:jobs].nil?
172
+ cmd += ['--ppn', cli[:threads]] unless cli[:threads].nil?
173
+ cwd = Dir.pwd
174
+ call_cli cmd
175
+ Dir.chdir(cwd)
176
+ end
177
+
178
+ end
@@ -5,11 +5,18 @@ module MiGA::Cli::Base
5
5
 
6
6
  @@TASK_DESC = {
7
7
  generic: 'MiGA: The Microbial Genomes Atlas',
8
+ # Workflows
9
+ quality_wf: 'Evaluate the quality of input genomes',
10
+ derep_wf: 'Dereplicate a collection of input genomes',
11
+ classify_wf: 'Classify input genomes against a reference database',
12
+ preproc_wf: 'Preprocess input genomes or metagenomes',
13
+ index_wf: 'Generate distance indexing of input genomes',
8
14
  # Projects
9
15
  new: 'Creates an empty MiGA project',
10
16
  about: 'Displays information about a MiGA project',
11
17
  plugins: 'Lists or (un)installs plugins in a MiGA project',
12
18
  doctor: 'Performs consistency checks on a MiGA project',
19
+ get_db: 'Downloads a pre-indexed database',
13
20
  # Datasets
14
21
  add: 'Creates a dataset in a MiGA project',
15
22
  get: 'Downloads a dataset from public databases into a MiGA project',
@@ -43,6 +50,7 @@ module MiGA::Cli::Base
43
50
  # Projects
44
51
  create_project: :new,
45
52
  project_info: :about,
53
+ download: :get_db,
46
54
  # Datasets
47
55
  create_dataset: :add,
48
56
  download_dataset: :get,
@@ -76,6 +84,8 @@ module MiGA::Cli::Base
76
84
 
77
85
  @@EXECS = @@TASK_DESC.keys
78
86
 
87
+ @@FILE_REGEXP = %r{^(?:.*/)?(.+?)(?:\..*(?:[12]|Reads|Contigs))?(?:\.f[nastq]+)?$}i
88
+
79
89
  end
80
90
 
81
91
  class MiGA::Cli < MiGA::MiGA
@@ -86,5 +96,6 @@ class MiGA::Cli < MiGA::MiGA
86
96
  def TASK_DESC; @@TASK_DESC end
87
97
  def TASK_ALIAS; @@TASK_ALIAS end
88
98
  def EXECS; @@EXECS end
99
+ def FILE_REGEXP; @@FILE_REGEXP end
89
100
  end
90
101
  end
@@ -0,0 +1,88 @@
1
+ # @package MiGA
2
+ # @license Artistic-2.0
3
+
4
+ module MiGA::Cli::ObjectsHelper
5
+ ##
6
+ # Get the project defined in the CLI by parameter +name+ and +flag+
7
+ def load_project(name = :project, flag = '-P')
8
+ return @objects[name] unless @objects[name].nil?
9
+ ensure_par(name => flag)
10
+ say "Loading project: #{self[name]}"
11
+ @objects[name] = MiGA::Project.load(self[name])
12
+ raise "Cannot load project: #{self[name]}" if @objects[name].nil?
13
+ @objects[name]
14
+ end
15
+
16
+ ##
17
+ # Load the dataset defined in the CLI
18
+ # If +silent=true+, it allows failures silently
19
+ def load_dataset(silent = false)
20
+ return @objects[:dataset] unless @objects[:dataset].nil?
21
+ ensure_par(dataset: '-D')
22
+ @objects[:dataset] = load_project.dataset(self[:dataset])
23
+ if !silent && @objects[:dataset].nil?
24
+ raise "Cannot load dataset: #{self[:dataset]}"
25
+ end
26
+ return @objects[:dataset]
27
+ end
28
+
29
+ ##
30
+ # Load an a project or (if defined) a dataset
31
+ def load_project_or_dataset
32
+ self[:dataset].nil? ? load_project : load_dataset
33
+ end
34
+
35
+ ##
36
+ # Load and filter a list of datasets as requested in the CLI
37
+ # If +silent=true+, it allows failures silently
38
+ def load_and_filter_datasets(silent = false)
39
+ return @objects[:filtered_datasets] unless @objects[:filtered_datasets].nil?
40
+ say 'Listing datasets'
41
+ ds = self[:dataset].nil? ?
42
+ load_project.datasets : [load_dataset(silent)].compact
43
+ ds.select! { |d| d.is_ref? == self[:ref] } unless self[:ref].nil?
44
+ ds.select! { |d| d.is_active? == self[:active] } unless self[:active].nil?
45
+ ds.select! do |d|
46
+ self[:multi] ? d.is_multi? : d.is_nonmulti?
47
+ end unless self[:multi].nil?
48
+ ds.select! do |d|
49
+ (not d.metadata[:tax].nil?) && d.metadata[:tax].in?(self[:taxonomy])
50
+ end unless self[:taxonomy].nil?
51
+ ds = ds.values_at(self[:dataset_k]-1) unless self[:dataset_k].nil?
52
+ @objects[:filtered_datasets] = ds
53
+ end
54
+
55
+ def load_result
56
+ return @objects[:result] unless @objects[:result].nil?
57
+ ensure_par(result: '-r')
58
+ obj = load_project_or_dataset
59
+ if obj.class.RESULT_DIRS[self[:result]].nil?
60
+ klass = obj.class.to_s.gsub(/.*::/,'')
61
+ raise "Unsupported result for #{klass}: #{self[:result]}"
62
+ end
63
+ r = obj.add_result(self[:result], false)
64
+ raise "Cannot load result: #{self[:result]}" if r.nil?
65
+ @objects[:result] = r
66
+ end
67
+
68
+ def add_metadata(obj, cli = self)
69
+ cli[:metadata].split(',').each do |pair|
70
+ (k,v) = pair.split('=')
71
+ case v
72
+ when 'true'; v = true
73
+ when 'false'; v = false
74
+ when 'nil'; v = nil
75
+ end
76
+ if k == '_step'
77
+ obj.metadata["_try_#{v}"] ||= 0
78
+ obj.metadata["_try_#{v}"] += 1
79
+ end
80
+ obj.metadata[k] = v
81
+ end unless cli[:metadata].nil?
82
+ [:type, :name, :user, :description, :comments].each do |k|
83
+ obj.metadata[k] = cli[k] unless cli[k].nil?
84
+ end
85
+ obj
86
+ end
87
+ end
88
+
@@ -0,0 +1,160 @@
1
+ # @package MiGA
2
+ # @license Artistic-2.0
3
+
4
+ module MiGA::Cli::OptHelper
5
+ ##
6
+ # Send MiGA's banner to OptionParser +opt+
7
+ def banner(opt)
8
+ usage = "Usage: miga #{action.name}"
9
+ usage += ' {operation}' if expect_operation
10
+ usage += ' [options]'
11
+ usage += ' {FILES...}' if expect_files
12
+ opt.banner = "\n#{task_description}\n\n#{usage}\n"
13
+ opt.separator ''
14
+ end
15
+
16
+ ##
17
+ # Common options at the end of most actions, passed to OptionParser +opt+
18
+ # No action is performed if +#opt_common = false+ is passed
19
+ # Executes only once, unless +#opt_common = true+ is passed between calls
20
+ def opt_common(opt)
21
+ return unless @opt_common
22
+ if interactive
23
+ opt.on(
24
+ '--auto',
25
+ 'Accept all defaults as answers'
26
+ ) { |v| self[:auto] = v }
27
+ end
28
+ opt.on(
29
+ '-v', '--verbose',
30
+ 'Print additional information to STDERR'
31
+ ) { |v| self[:verbose] = v }
32
+ opt.on(
33
+ '-d', '--debug INT', Integer,
34
+ 'Print debugging information to STDERR (1: debug, 2: trace)'
35
+ ) { |v| v > 1 ? MiGA::MiGA.DEBUG_TRACE_ON : MiGA::MiGA.DEBUG_ON }
36
+ opt.on(
37
+ '-h', '--help',
38
+ 'Display this screen'
39
+ ) do
40
+ puts opt
41
+ exit
42
+ end
43
+ opt.separator ''
44
+ self.opt_common = false
45
+ end
46
+
47
+ ##
48
+ # Options to load an object passed to OptionParser +opt+, as determined
49
+ # by +what+ an Array with any combination of:
50
+ # - :project To require a project
51
+ # - :dataset To require a dataset
52
+ # - :dataset_opt To allow (optionally) a dataset
53
+ # - :dataset_type To allow (optionally) a type of dataset
54
+ # - :dataset_type_req To require a type of dataset
55
+ # - :project_type To allow (optionally) a type of project
56
+ # - :project_type_req To require a type of project
57
+ # - :result To require a type of project or dataset result
58
+ # - :result_dataset To require a type of dataset result
59
+ # - :result_project To require a type of project result
60
+ # The options :result, :result_dataset, and :result_project are mutually
61
+ # exclusive
62
+ def opt_object(opt, what = [:project, :dataset])
63
+ what.each do |w|
64
+ case w
65
+ when :project
66
+ opt.on(
67
+ '-P', '--project PATH',
68
+ '(Mandatory) Path to the project'
69
+ ) { |v| self[:project] = v }
70
+ when :dataset, :dataset_opt
71
+ opt.on(
72
+ '-D', '--dataset STRING',
73
+ (w == :dataset ? '(Mandatory) ' : '') + 'Name of the dataset'
74
+ ) { |v| self[:dataset] = v }
75
+ when :dataset_type, :dataset_type_req, :project_type, :project_type_req
76
+ obj = w.to_s.gsub(/_.*/, '')
77
+ klass = Object.const_get("MiGA::#{obj.capitalize}")
78
+ req = w.to_s =~ /_req$/ ? '(Mandatory) ' : ''
79
+ opt.on(
80
+ '-t', '--type STRING',
81
+ "#{req}Type of #{obj}. Recognized types include:",
82
+ *klass.KNOWN_TYPES.map { |k, v| "~ #{k}: #{v[:description]}" }
83
+ ) { |v| self[:type] = v.downcase.to_sym }
84
+ when :result
85
+ opt.on(
86
+ '-r', '--result STRING',
87
+ '(Mandatory) Name of the result',
88
+ 'Recognized names for dataset-specific results include:',
89
+ *MiGA::Dataset.RESULT_DIRS.keys.map { |n| " ~ #{n}" },
90
+ 'Recognized names for project-wide results include:',
91
+ *MiGA::Project.RESULT_DIRS.keys.map { |n| " ~ #{n}" }
92
+ ) { |v| self[:result] = v.downcase.to_sym }
93
+ when :result_dataset
94
+ opt.on(
95
+ '-r', '--result STRING',
96
+ '(Mandatory) Name of the result, one of:',
97
+ *MiGA::Dataset.RESULT_DIRS.keys.map { |n| " ~ #{n}" }
98
+ ) { |v| self[:result] = v.downcase.to_sym }
99
+ when :result_project
100
+ opt.on(
101
+ '-r', '--result STRING',
102
+ '(Mandatory) Name of the result, one of:',
103
+ *MiGA::Project.RESULT_DIRS.keys.map { |n| " ~ #{n}" }
104
+ ) { |v| self[:result] = v.downcase.to_sym }
105
+ else
106
+ raise "Internal error: Unrecognized option: #{w}"
107
+ end
108
+ end
109
+ end
110
+
111
+ ##
112
+ # Options to filter a list of datasets passed to OptionParser +opt+,
113
+ # as determined by +what+ an Array with any combination of:
114
+ # - :ref To filter by reference (--ref) or query (--no-ref)
115
+ # - :multi To filter by multiple (--multi) or single (--no-multi) species
116
+ # - :active To filter by active (--active) or inactive (--no-active)
117
+ # - :taxonomy To filter by taxonomy (--taxonomy)
118
+ # The "k-th" filter (--dataset-k) is always included
119
+ def opt_filter_datasets(opt, what = [:ref, :multi, :active, :taxonomy])
120
+ what.each do |w|
121
+ case w
122
+ when :ref
123
+ opt.on(
124
+ '--[no-]ref',
125
+ 'Use only reference (or only non-reference) datasets'
126
+ ) { |v| self[:ref] = v }
127
+ when :multi
128
+ opt.on(
129
+ '--[no-]multi',
130
+ 'Use only multi-species (or only single-species) datasets'
131
+ ) { |v| self[:multi] = v }
132
+ when :active
133
+ opt.on(
134
+ '--[no-]active',
135
+ 'Use only active (or inactive) datasets'
136
+ ) { |v| self[:active] = v }
137
+ when :taxonomy
138
+ opt.on(
139
+ '-t', '--taxonomy RANK:TAXON',
140
+ 'Filter by taxonomy'
141
+ ) { |v| self[:taxonomy] = MiGA::Taxonomy.new(v) }
142
+ else
143
+ raise "Internal error: Unrecognized option: #{w}"
144
+ end
145
+ end
146
+ opt.on(
147
+ '--dataset-k INTEGER', Integer,
148
+ 'Use only the k-th dataset in the list'
149
+ ) { |v| self[:dataset_k] = v }
150
+ end
151
+
152
+ ##
153
+ # Add a flag (true/false) to the OptionParser +opt+ defined by
154
+ # +flag+ (without --) and +description+, and save it in the CLI as +sym+.
155
+ # If +sym+ is nil, +flag+ is used as Symbol
156
+ def opt_flag(opt, flag, description, sym = nil)
157
+ sym = flag.to_sym if sym.nil?
158
+ opt.on("--#{flag}", description) { |v| self[sym] = v }
159
+ end
160
+ end