rbbt-util 5.2.4 → 5.3.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 +8 -8
- data/bin/rbbt +23 -10
- data/bin/rbbt_monitor.rb +8 -8
- data/lib/rbbt/annotations.rb +22 -1
- data/lib/rbbt/annotations/util.rb +1 -1
- data/lib/rbbt/entity.rb +162 -0
- data/lib/rbbt/fix_width_table.rb +7 -0
- data/lib/rbbt/persist.rb +16 -9
- data/lib/rbbt/persist/tsv.rb +14 -8
- data/lib/rbbt/resource.rb +1 -6
- data/lib/rbbt/resource/path.rb +23 -27
- data/lib/rbbt/tsv.rb +33 -4
- data/lib/rbbt/tsv/accessor.rb +100 -57
- data/lib/rbbt/tsv/attach.rb +3 -1
- data/lib/rbbt/tsv/attach/util.rb +34 -10
- data/lib/rbbt/tsv/index.rb +12 -3
- data/lib/rbbt/tsv/manipulate.rb +25 -1
- data/lib/rbbt/tsv/parser.rb +1 -0
- data/lib/rbbt/util/R.rb +36 -6
- data/lib/rbbt/util/cmd.rb +2 -1
- data/lib/rbbt/util/color.rb +250 -0
- data/lib/rbbt/util/colorize.rb +57 -0
- data/lib/rbbt/util/misc.rb +57 -19
- data/lib/rbbt/util/named_array.rb +66 -14
- data/lib/rbbt/util/open.rb +134 -10
- data/lib/rbbt/util/semaphore.rb +71 -0
- data/lib/rbbt/workflow.rb +34 -7
- data/lib/rbbt/workflow/accessor.rb +12 -8
- data/lib/rbbt/workflow/step.rb +44 -28
- data/lib/rbbt/workflow/usage.rb +3 -0
- data/share/lib/R/util.R +31 -0
- data/share/rbbt_commands/app/start +5 -4
- data/share/rbbt_commands/study/task +222 -0
- data/share/rbbt_commands/tsv/attach +13 -0
- data/share/rbbt_commands/tsv/change_id +15 -0
- data/share/rbbt_commands/tsv/info +3 -1
- data/share/rbbt_commands/workflow/task +14 -15
- data/test/rbbt/test_entity.rb +221 -0
- data/test/rbbt/test_tsv.rb +2 -1
- data/test/rbbt/test_workflow.rb +0 -2
- data/test/rbbt/tsv/test_accessor.rb +2 -2
- data/test/rbbt/util/test_R.rb +9 -2
- data/test/rbbt/util/test_colorize.rb +12 -0
- data/test/rbbt/util/test_misc.rb +0 -5
- data/test/rbbt/util/test_open.rb +31 -0
- data/test/rbbt/workflow/test_step.rb +32 -0
- metadata +13 -2
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'inline'
|
2
|
+
|
3
|
+
module RbbtSemaphore
|
4
|
+
inline(:C) do |builder|
|
5
|
+
builder.prefix <<-EOF
|
6
|
+
#include <unistd.h>
|
7
|
+
#include <stdio.h>
|
8
|
+
#include <stdlib.h>
|
9
|
+
#include <semaphore.h>
|
10
|
+
#include <time.h>
|
11
|
+
#include <assert.h>
|
12
|
+
#include <errno.h>
|
13
|
+
#include <signal.h>
|
14
|
+
#include <fcntl.h>
|
15
|
+
EOF
|
16
|
+
|
17
|
+
builder.c_singleton <<-EOF
|
18
|
+
void create_semaphore(char* name, int value){
|
19
|
+
sem_open(name, O_CREAT, S_IRWXU, value);
|
20
|
+
}
|
21
|
+
EOF
|
22
|
+
builder.c_singleton <<-EOF
|
23
|
+
void delete_semaphore(char* name){
|
24
|
+
sem_unlink(name);
|
25
|
+
}
|
26
|
+
EOF
|
27
|
+
|
28
|
+
builder.c_singleton <<-EOF
|
29
|
+
void wait_semaphore(char* name){
|
30
|
+
sem_t* sem;
|
31
|
+
sem = sem_open(name, O_EXCL);
|
32
|
+
sem_wait(sem);
|
33
|
+
sem_close(sem);
|
34
|
+
}
|
35
|
+
EOF
|
36
|
+
|
37
|
+
builder.c_singleton <<-EOF
|
38
|
+
void post_semaphore(char* name){
|
39
|
+
sem_t* sem;
|
40
|
+
sem = sem_open(name, O_EXCL);
|
41
|
+
sem_post(sem);
|
42
|
+
sem_close(sem);
|
43
|
+
}
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.with_semaphore(size, file = nil)
|
48
|
+
file = Misc.digest(rand.to_s) if file.nil?
|
49
|
+
file.gsub!('/', '_')
|
50
|
+
begin
|
51
|
+
RbbtSemaphore.create_semaphore(file, size)
|
52
|
+
yield file
|
53
|
+
ensure
|
54
|
+
RbbtSemaphore.delete_semaphore(file)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.fork_each_on_semaphore(elems, size, file = nil)
|
59
|
+
with_semaphore(size, file) do |file|
|
60
|
+
pids = elems.collect do |elem|
|
61
|
+
Process.fork do
|
62
|
+
RbbtSemaphore.wait_semaphore(file)
|
63
|
+
yield elem
|
64
|
+
RbbtSemaphore.post_semaphore(file)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
pids.each do |pid| Process.waitpid pid end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -58,8 +58,9 @@ module Workflow
|
|
58
58
|
else
|
59
59
|
case
|
60
60
|
# Points to workflow file
|
61
|
-
when ((File.exists?(wf_name) and not File.directory?(wf_name)) or File.exists?(wf_name + '.rb'))
|
61
|
+
when ((File.exists?(wf_name) and not File.directory?(wf_name)) or File.exists?(wf_name + '.rb') or File.exists?(wf_name))
|
62
62
|
$LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(wf_name)), 'lib'))
|
63
|
+
wf_name = "./" << wf_name unless wf_name[0] == "/"
|
63
64
|
require wf_name
|
64
65
|
Log.medium "Workflow loaded from file: #{ wf_name }"
|
65
66
|
return true
|
@@ -112,19 +113,17 @@ module Workflow
|
|
112
113
|
begin
|
113
114
|
require_local_workflow(wf_name)
|
114
115
|
rescue Exception
|
115
|
-
Log.debug $!.message
|
116
|
-
Log.debug $!.backtrace.first
|
117
116
|
raise "Workflow not found: #{ wf_name }" if wf_name == Misc.snake_case(wf_name)
|
118
|
-
Log.debug "Trying with humanized: '#{Misc.snake_case wf_name}'"
|
119
117
|
begin
|
120
118
|
require_local_workflow(Misc.snake_case(wf_name))
|
121
119
|
rescue Exception
|
122
|
-
Log.
|
123
|
-
raise
|
120
|
+
Log.error("Workflow not found: #{ wf_name }")
|
121
|
+
raise $!
|
124
122
|
end
|
125
123
|
end
|
126
124
|
end
|
127
125
|
|
126
|
+
attr_accessor :description
|
128
127
|
attr_accessor :libdir, :workdir
|
129
128
|
attr_accessor :helpers, :tasks
|
130
129
|
attr_accessor :task_dependencies, :task_description, :last_task
|
@@ -134,7 +133,8 @@ module Workflow
|
|
134
133
|
|
135
134
|
def workdir
|
136
135
|
@workdir ||= if defined? Rbbt
|
137
|
-
|
136
|
+
text = Module === self ? self.to_s : "Misc"
|
137
|
+
Rbbt.var.jobs[text].find
|
138
138
|
else
|
139
139
|
Path.setup('var/jobs')
|
140
140
|
end
|
@@ -144,6 +144,17 @@ module Workflow
|
|
144
144
|
@libdir = Path.caller_lib_dir if @libdir.nil?
|
145
145
|
@libdir
|
146
146
|
end
|
147
|
+
|
148
|
+
def workflow_description
|
149
|
+
@workflow_description ||= begin
|
150
|
+
file = @libdir['workflow.md']
|
151
|
+
if file.exists?
|
152
|
+
file.read
|
153
|
+
else
|
154
|
+
""
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
147
158
|
|
148
159
|
def helpers
|
149
160
|
@helpers ||= {}
|
@@ -232,4 +243,20 @@ module Workflow
|
|
232
243
|
Misc.path_relative_to(task_dir, f).sub(".info",'')
|
233
244
|
}
|
234
245
|
end
|
246
|
+
|
247
|
+
def local_persist_setup
|
248
|
+
class << self
|
249
|
+
include LocalPersist
|
250
|
+
end
|
251
|
+
self.local_persist_dir = Rbbt.var.cache.persistence.find :lib
|
252
|
+
end
|
253
|
+
|
254
|
+
def local_workdir_setup
|
255
|
+
self.workdir = Rbbt.var.jobs.find :lib
|
256
|
+
end
|
257
|
+
|
258
|
+
def make_local
|
259
|
+
local_persist_setup
|
260
|
+
local_workdir_setup
|
261
|
+
end
|
235
262
|
end
|
@@ -2,6 +2,8 @@ require 'rbbt/util/open'
|
|
2
2
|
require 'yaml'
|
3
3
|
|
4
4
|
class Step
|
5
|
+
|
6
|
+
INFO_SERIALIAZER = Marshal
|
5
7
|
|
6
8
|
def name
|
7
9
|
@path.sub(/.*\/#{Regexp.quote task.name.to_s}\/(.*)/, '\1')
|
@@ -22,22 +24,24 @@ class Step
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def info
|
25
|
-
return {} if not
|
27
|
+
return {} if not Open.exists? info_file
|
26
28
|
begin
|
27
|
-
|
28
|
-
|
29
|
+
Open.open(info_file) do |file|
|
30
|
+
INFO_SERIALIAZER.load(file) || {}
|
29
31
|
end
|
30
32
|
rescue Exception
|
31
|
-
Log.debug "Error loading
|
33
|
+
Log.debug "Error loading info file: " + info_file
|
32
34
|
raise $!
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
38
|
def set_info(key, value)
|
37
|
-
|
39
|
+
return nil if @exec
|
40
|
+
value = Annotated.purge value
|
41
|
+
Open.lock(info_file) do
|
38
42
|
i = info
|
39
43
|
i[key] = value
|
40
|
-
Open.write(info_file, i
|
44
|
+
Open.write(info_file, INFO_SERIALIAZER.dump(i))
|
41
45
|
value
|
42
46
|
end
|
43
47
|
end
|
@@ -71,7 +75,7 @@ class Step
|
|
71
75
|
end
|
72
76
|
|
73
77
|
def started?
|
74
|
-
|
78
|
+
Open.exists? info_file
|
75
79
|
end
|
76
80
|
|
77
81
|
def done?
|
@@ -79,7 +83,7 @@ class Step
|
|
79
83
|
end
|
80
84
|
|
81
85
|
def running?
|
82
|
-
return nil if not
|
86
|
+
return nil if not Open.exists? info_file
|
83
87
|
return nil if info[:pid].nil?
|
84
88
|
return Misc.pid_exists? info[:pid]
|
85
89
|
end
|
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'rbbt/persist'
|
2
2
|
require 'rbbt/persist/tsv'
|
3
3
|
require 'rbbt/util/log'
|
4
|
+
require 'rbbt/util/semaphore'
|
4
5
|
require 'rbbt/workflow/accessor'
|
5
6
|
|
6
7
|
class Step
|
7
8
|
attr_accessor :path, :task, :inputs, :dependencies, :bindings
|
8
9
|
attr_accessor :pid
|
10
|
+
attr_accessor :exec
|
9
11
|
|
10
12
|
class Aborted < Exception; end
|
11
13
|
|
@@ -62,6 +64,7 @@ class Step
|
|
62
64
|
end
|
63
65
|
|
64
66
|
def exec
|
67
|
+
@exec = true if @exec.nil?
|
65
68
|
result = @task.exec_in((bindings ? bindings : self), *@inputs)
|
66
69
|
prepare_result result, @task.result_description
|
67
70
|
end
|
@@ -83,11 +86,12 @@ class Step
|
|
83
86
|
|
84
87
|
def run(no_load = false)
|
85
88
|
result = Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path }.uniq, :no_load => no_load do
|
89
|
+
@exec = false
|
86
90
|
if Step === Step.log_relay_step and not self == Step.log_relay_step
|
87
91
|
relay_log(Step.log_relay_step) unless self.respond_to? :relay_step and self.relay_step
|
88
92
|
end
|
89
93
|
|
90
|
-
|
94
|
+
Open.rm info_file if Open.exists? info_file
|
91
95
|
|
92
96
|
set_info :pid, Process.pid
|
93
97
|
|
@@ -95,6 +99,7 @@ class Step
|
|
95
99
|
dependencies.each{|dependency|
|
96
100
|
begin
|
97
101
|
dependency.relay_log self
|
102
|
+
dependency.clean if not dependency.done? and dependency.error?
|
98
103
|
dependency.run true
|
99
104
|
rescue Exception
|
100
105
|
backtrace = $!.backtrace
|
@@ -158,37 +163,43 @@ class Step
|
|
158
163
|
end
|
159
164
|
end
|
160
165
|
|
161
|
-
def fork
|
166
|
+
def fork(semaphore = nil)
|
162
167
|
raise "Can not fork: Step is waiting for proces #{@pid} to finish" if not @pid.nil?
|
163
168
|
@pid = Process.fork do
|
164
169
|
trap(:INT) { raise Step::Aborted.new "INT signal recieved" }
|
165
|
-
FileUtils.mkdir_p File.dirname(path) unless File.exists? File.dirname(path)
|
166
170
|
begin
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
171
|
+
RbbtSemaphore.wait_semaphore(semaphore) if semaphore
|
172
|
+
FileUtils.mkdir_p File.dirname(path) unless Open.exists? File.dirname(path)
|
173
|
+
begin
|
174
|
+
run(true)
|
175
|
+
rescue Exception
|
176
|
+
Log.debug("Exception caught on forked process: #{$!.message}")
|
177
|
+
exit -1
|
178
|
+
end
|
172
179
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
180
|
+
begin
|
181
|
+
children_pids = info[:children_pids]
|
182
|
+
if children_pids
|
183
|
+
children_pids.each do |pid|
|
184
|
+
if Misc.pid_exists? pid
|
185
|
+
begin
|
186
|
+
Process.waitpid pid
|
187
|
+
rescue Errno::ECHILD
|
188
|
+
Log.error "Waiting on #{ pid } failed: #{$!.message}"
|
189
|
+
end
|
182
190
|
end
|
183
191
|
end
|
192
|
+
set_info :children_done, Time.now
|
184
193
|
end
|
194
|
+
rescue Exception
|
195
|
+
Log.debug("Exception waiting for children: #{$!.message}")
|
196
|
+
exit -1
|
185
197
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
198
|
+
set_info :pid, nil
|
199
|
+
exit 0
|
200
|
+
ensure
|
201
|
+
RbbtSemaphore.post_semaphore(semaphore) if semaphore
|
189
202
|
end
|
190
|
-
set_info :pid, nil
|
191
|
-
exit 0
|
192
203
|
end
|
193
204
|
Process.detach(@pid)
|
194
205
|
self
|
@@ -234,13 +245,13 @@ class Step
|
|
234
245
|
end
|
235
246
|
|
236
247
|
def clean
|
237
|
-
if
|
248
|
+
if Open.exists?(path) or Open.exists?(info_file)
|
238
249
|
begin
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
250
|
+
Open.rm info_file if Open.exists? info_file
|
251
|
+
Open.rm info_file + '.lock' if Open.exists? info_file + '.lock'
|
252
|
+
Open.rm path if Open.exists? path
|
253
|
+
Open.rm path + '.lock' if Open.exists? path + '.lock'
|
254
|
+
Open.rm_rf files_dir if Open.exists? files_dir
|
244
255
|
end
|
245
256
|
end
|
246
257
|
self
|
@@ -250,6 +261,11 @@ class Step
|
|
250
261
|
@dependencies.collect{|step| step.rec_dependencies}.flatten.concat @dependencies
|
251
262
|
end
|
252
263
|
|
264
|
+
def recursive_clean
|
265
|
+
rec_dependencies.each{|step| step.clean }
|
266
|
+
clean
|
267
|
+
end
|
268
|
+
|
253
269
|
def step(name)
|
254
270
|
rec_dependencies.select{|step| step.task.name.to_sym == name.to_sym}.first
|
255
271
|
end
|
data/lib/rbbt/workflow/usage.rb
CHANGED
@@ -15,6 +15,7 @@ module Task
|
|
15
15
|
puts " #{dep.name}:"
|
16
16
|
puts
|
17
17
|
puts SOPT.input_doc(dep.inputs, dep.input_types, dep.input_descriptions, dep.input_defaults)
|
18
|
+
puts
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
@@ -27,6 +28,8 @@ module Workflow
|
|
27
28
|
puts self.to_s
|
28
29
|
puts "=" * self.to_s.length
|
29
30
|
puts
|
31
|
+
puts "\n" << workflow_description if workflow_description and not workflow_description.empty?
|
32
|
+
puts
|
30
33
|
|
31
34
|
puts "## TASKS"
|
32
35
|
puts
|
data/share/lib/R/util.R
CHANGED
@@ -167,7 +167,38 @@ rbbt.acc <- function(data, new){
|
|
167
167
|
rbbt.png_plot <- function(filename, width, height, p, ...){
|
168
168
|
png(filename=filename, width=width, height=height, ...);
|
169
169
|
eval(parse(text=p));
|
170
|
+
}
|
171
|
+
|
172
|
+
rbbt.heatmap <- function(filename, width, height, data, take_log=FALSE, ...){
|
173
|
+
require(gplots, quietly = TRUE, warn.conflicts = FALSE)
|
174
|
+
library(pls, quietly = TRUE, warn.conflicts = FALSE)
|
175
|
+
opar = par()
|
176
|
+
png(filename=filename, width=width, height=height);
|
177
|
+
|
178
|
+
#par(cex.lab=0.5, cex=0.5, ...)
|
179
|
+
|
180
|
+
data = as.matrix(data)
|
181
|
+
data[is.nan(data)] = NA
|
182
|
+
|
183
|
+
#data = data[rowSums(!is.na(data))!=0, colSums(!is.na(data))!=0]
|
184
|
+
data = data[rowSums(is.na(data))==0, ]
|
185
|
+
|
186
|
+
if (take_log){
|
187
|
+
for (study in colnames(data)){
|
188
|
+
skip = sum(data[, study] <= 0) != 0
|
189
|
+
if (!skip){
|
190
|
+
data[, study] = log(data[, study])
|
191
|
+
}
|
192
|
+
}
|
193
|
+
data = data[, colSums(is.na(data))==0]
|
194
|
+
}
|
195
|
+
|
196
|
+
data = stdize(data)
|
197
|
+
|
198
|
+
heatmap.2(data, margins = c(20,5), scale='column')
|
199
|
+
|
170
200
|
dev.off();
|
201
|
+
par(opar)
|
171
202
|
}
|
172
203
|
|
173
204
|
rbbt.init <- function(data, new){
|
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'rbbt-util'
|
4
4
|
require 'rbbt/util/simpleopt'
|
5
5
|
|
6
|
-
options = SOPT.get "-e--environment*:-p--port*:-s--server
|
6
|
+
options = SOPT.get "-e--environment*:-p--port*:-s--server*:-f--finder"
|
7
|
+
options[:Port] ||= options[:port]
|
7
8
|
|
8
9
|
app = ARGV.shift
|
9
10
|
|
@@ -12,8 +13,8 @@ app_dir = Rbbt.etc.app_dir.exists? ? Path.setup(Rbbt.etc.app_dir.read.strip) : R
|
|
12
13
|
app_dir = app_dir[app]
|
13
14
|
|
14
15
|
server = options[:server] || 'thin'
|
15
|
-
|
16
16
|
Misc.in_dir(app_dir) do
|
17
|
-
|
18
|
-
|
17
|
+
require 'rack'
|
18
|
+
ENV["RBBT_FINDER"] = true if options.include?(:finder)
|
19
|
+
Rack::Server.start(options.merge(:config => 'config.ru'))
|
19
20
|
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
require 'rbbt/util/simpleopt'
|
5
|
+
require 'rbbt/workflow'
|
6
|
+
require 'rbbt/workflow/usage'
|
7
|
+
require 'rbbt/entity/study'
|
8
|
+
require 'rbbt/entity/study/genotypes'
|
9
|
+
require 'rbbt/entity/study/cnv'
|
10
|
+
require 'rbbt/entity/study/expression'
|
11
|
+
|
12
|
+
def usage(workflow = nil, task = nil)
|
13
|
+
puts SOPT.doc
|
14
|
+
puts "## WORKFLOW"
|
15
|
+
puts
|
16
|
+
if workflow.nil?
|
17
|
+
puts "No workflow specified"
|
18
|
+
exit -1
|
19
|
+
end
|
20
|
+
|
21
|
+
if task.nil?
|
22
|
+
workflow.load_tasks if workflow.respond_to? :load_tasks
|
23
|
+
workflow.doc
|
24
|
+
else
|
25
|
+
puts workflow.to_s
|
26
|
+
puts "=" * workflow.to_s.length
|
27
|
+
puts
|
28
|
+
puts workflow.workflow_description
|
29
|
+
puts
|
30
|
+
workflow.doc(task)
|
31
|
+
end
|
32
|
+
|
33
|
+
exit 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def SOPT_options(workflow, task)
|
37
|
+
sopt_options = []
|
38
|
+
workflow.rec_inputs(task.name).each do |name|
|
39
|
+
short = name.to_s.chars.first
|
40
|
+
boolean = workflow.rec_input_types(task.name)[name].to_sym == :boolean
|
41
|
+
|
42
|
+
sopt_options << "-#{short}--#{name}#{boolean ? '' : '*'}"
|
43
|
+
end
|
44
|
+
|
45
|
+
sopt_options * ":"
|
46
|
+
end
|
47
|
+
|
48
|
+
def fix_options(workflow, task, job_options)
|
49
|
+
option_types = workflow.rec_input_types(task.name)
|
50
|
+
|
51
|
+
job_options_cleaned = {}
|
52
|
+
|
53
|
+
job_options.each do |name, value|
|
54
|
+
value = case option_types[name].to_sym
|
55
|
+
when :float
|
56
|
+
value.to_f
|
57
|
+
when :integer
|
58
|
+
value.to_i
|
59
|
+
when :string, :text
|
60
|
+
case
|
61
|
+
when value == '-'
|
62
|
+
STDIN.read
|
63
|
+
when (String === value and File.exists?(value) and not File.directory?(value))
|
64
|
+
Open.read(value)
|
65
|
+
else
|
66
|
+
value
|
67
|
+
end
|
68
|
+
when :array
|
69
|
+
if Array === value
|
70
|
+
value
|
71
|
+
else
|
72
|
+
str = case
|
73
|
+
when value == '-'
|
74
|
+
STDIN.read
|
75
|
+
when (String === value and File.exists?(value))
|
76
|
+
Open.read(value)
|
77
|
+
else
|
78
|
+
value
|
79
|
+
end
|
80
|
+
|
81
|
+
if $array_separator
|
82
|
+
str.split(/#{$array_separator}/)
|
83
|
+
else
|
84
|
+
str.split(/[,|\s]/)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
when :tsv
|
88
|
+
case value
|
89
|
+
when TSV
|
90
|
+
value
|
91
|
+
when '-'
|
92
|
+
TSV.open(STDIN)
|
93
|
+
else
|
94
|
+
TSV.open(value)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
value
|
98
|
+
end
|
99
|
+
job_options_cleaned[name] = value
|
100
|
+
end
|
101
|
+
|
102
|
+
job_options_cleaned
|
103
|
+
end
|
104
|
+
|
105
|
+
options = SOPT.get <<EOF
|
106
|
+
-h--help Show this help:
|
107
|
+
-as--array_separator* Change the character that separates elements of Arrays, ',', '|', or '\\n' by default:
|
108
|
+
-cl--clean Clean the last step of the job so that it gets recomputed:
|
109
|
+
-rcl--recursive_clean Clean the last step and its dependencies to recompute the job completely:
|
110
|
+
-n--name* Job name to use. The name 'Default' is used by default:
|
111
|
+
-pn--printname Print the name of the job and exit without starting it:
|
112
|
+
-rw--require_workflow* Workflows to require, separated by commas
|
113
|
+
EOF
|
114
|
+
|
115
|
+
(options[:require_workflow] || "").split(',').each do |workflow|
|
116
|
+
Workflow.require_workflow workflow
|
117
|
+
end
|
118
|
+
|
119
|
+
study = ARGV.shift.dup
|
120
|
+
if Open.exists? study
|
121
|
+
dir = study
|
122
|
+
study = Study.setup(File.basename(study))
|
123
|
+
study.dir = Path.setup(dir, Study)
|
124
|
+
else
|
125
|
+
Study.setup(study)
|
126
|
+
end
|
127
|
+
workflow = study.workflow
|
128
|
+
|
129
|
+
usage if workflow.nil?
|
130
|
+
|
131
|
+
task = ARGV.shift
|
132
|
+
|
133
|
+
# Set log, fork, clean, recursive_clean and help
|
134
|
+
help = !!options.delete(:help)
|
135
|
+
do_fork = !!options.delete(:fork)
|
136
|
+
do_exec = !!options.delete(:exec)
|
137
|
+
clean = !!options.delete(:clean)
|
138
|
+
recursive_clean = !!options.delete(:recursive_clean)
|
139
|
+
$array_separator = options.delete(:array_separator)
|
140
|
+
|
141
|
+
# Set task
|
142
|
+
namespace = nil, nil
|
143
|
+
|
144
|
+
case
|
145
|
+
when task.nil?
|
146
|
+
usage workflow
|
147
|
+
when (task =~ /\./)
|
148
|
+
namespace, task = options.delete(:task).split('.')
|
149
|
+
namespace = Misc.string2const(namespace)
|
150
|
+
else
|
151
|
+
task_name = task.to_sym
|
152
|
+
task = workflow.tasks[task_name]
|
153
|
+
raise "Task not found: #{ task_name }" if task.nil?
|
154
|
+
end
|
155
|
+
|
156
|
+
usage workflow, task if help
|
157
|
+
|
158
|
+
name = options.delete(:name) || "Default"
|
159
|
+
|
160
|
+
# get job args
|
161
|
+
sopt_option_string = SOPT_options(workflow, task)
|
162
|
+
job_options = SOPT.get sopt_option_string
|
163
|
+
job_options = fix_options(workflow, task, job_options)
|
164
|
+
|
165
|
+
#- get job
|
166
|
+
|
167
|
+
job = study.job(task.name, job_options)
|
168
|
+
|
169
|
+
# clean job
|
170
|
+
if clean and job.done? != false
|
171
|
+
job.clean
|
172
|
+
sleep 1
|
173
|
+
job = study.job(task.name, job_options)
|
174
|
+
end
|
175
|
+
|
176
|
+
if recursive_clean and job.done?
|
177
|
+
job.recursive_clean
|
178
|
+
sleep 1
|
179
|
+
job = study.job(task.name, job_options)
|
180
|
+
end
|
181
|
+
|
182
|
+
# run
|
183
|
+
if do_exec
|
184
|
+
res = job.exec
|
185
|
+
case
|
186
|
+
when Array === res
|
187
|
+
puts res * "\n"
|
188
|
+
when TSV === res
|
189
|
+
puts res
|
190
|
+
when Hash === res
|
191
|
+
puts res.to_yaml
|
192
|
+
else
|
193
|
+
puts res
|
194
|
+
end
|
195
|
+
exit 0
|
196
|
+
end
|
197
|
+
|
198
|
+
if do_fork
|
199
|
+
job.fork
|
200
|
+
while not job.done?
|
201
|
+
Log.debug "#{job.step}: #{job.messages.last}"
|
202
|
+
sleep 2
|
203
|
+
end
|
204
|
+
raise job.messages.last if job.error?
|
205
|
+
else
|
206
|
+
res = job.run(true)
|
207
|
+
end
|
208
|
+
|
209
|
+
if options.delete(:printname)
|
210
|
+
puts job.name
|
211
|
+
exit 0
|
212
|
+
else
|
213
|
+
Log.low "Job name: #{job.name}"
|
214
|
+
end
|
215
|
+
|
216
|
+
if Step === res
|
217
|
+
puts Open.read(res.path) if File.exists? res.path
|
218
|
+
else
|
219
|
+
puts res
|
220
|
+
end
|
221
|
+
|
222
|
+
exit 0
|