transcriptic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ module Transcriptic::Command
2
+
3
+ # take interactive control of a lab context
4
+ #
5
+ class Console < Base
6
+
7
+ def index; end
8
+
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module Transcriptic::Command
2
+
3
+ # manage experimental data sets
4
+ #
5
+ class Data < Base
6
+ def index; end
7
+ end
8
+ end
@@ -0,0 +1,124 @@
1
+ module Transcriptic::Command
2
+
3
+ # list commands and display help
4
+ #
5
+ class Help < Base
6
+ PRIMARY_NAMESPACES = %w( analyze data history run status sequences)
7
+
8
+ # help [COMMAND]
9
+ #
10
+ # list available commands or display help for a specific command
11
+ #
12
+ def index
13
+ if command = args.shift
14
+ help_for_command(command)
15
+ else
16
+ help_for_root
17
+ end
18
+ end
19
+
20
+ alias_command "-h", "help"
21
+ alias_command "--help", "help"
22
+
23
+ def self.usage_for_command(command)
24
+ command = new.send(:commands)[command]
25
+ "Usage: transcriptic #{command[:banner]}" if command
26
+ end
27
+
28
+ private
29
+
30
+ def commands_for_namespace(name)
31
+ Transcriptic::Command.commands.values.select do |command|
32
+ command[:namespace] == name && command[:command] != name
33
+ end
34
+ end
35
+
36
+ def namespaces
37
+ namespaces = Transcriptic::Command.namespaces
38
+ namespaces.delete("app")
39
+ namespaces
40
+ end
41
+
42
+ def commands
43
+ Transcriptic::Command.commands
44
+ end
45
+
46
+ def legacy_help_for_namespace(namespace)
47
+ instance = Transcriptic::Command::Help.groups.map do |group|
48
+ [ group.title, group.select { |c| c.first =~ /^#{namespace}/ }.length ]
49
+ end.sort_by { |l| l.last }.last
50
+ return nil unless instance
51
+ return nil if instance.last.zero?
52
+ instance.first
53
+ end
54
+
55
+ def primary_namespaces
56
+ PRIMARY_NAMESPACES.map { |name| namespaces[name] }.compact
57
+ end
58
+
59
+ def additional_namespaces
60
+ (namespaces.values - primary_namespaces)
61
+ end
62
+
63
+ def summary_for_namespaces(namespaces)
64
+ size = longest(namespaces.map { |n| n[:name] })
65
+ namespaces.sort_by {|namespace| namespace[:name]}.each do |namespace|
66
+ name = namespace[:name]
67
+ namespace[:description]
68
+ puts " %-#{size}s # %s" % [ name, namespace[:description] ]
69
+ end
70
+ end
71
+
72
+ def help_for_root
73
+ puts "Usage: transcriptic COMMAND [command-specific-options]"
74
+ puts
75
+ puts "Primary help topics, type \"transcriptic help TOPIC\" for more details:"
76
+ puts
77
+ summary_for_namespaces(primary_namespaces)
78
+ puts
79
+ puts "Additional topics:"
80
+ puts
81
+ summary_for_namespaces(additional_namespaces)
82
+ puts
83
+ end
84
+
85
+ def help_for_namespace(name)
86
+ namespace_commands = commands_for_namespace(name)
87
+
88
+ unless namespace_commands.empty?
89
+ size = longest(namespace_commands.map { |c| c[:banner] })
90
+ namespace_commands.sort_by { |c| c[:banner].to_s }.each do |command|
91
+ next if command[:help] =~ /DEPRECATED/
92
+ command[:summary]
93
+ puts " %-#{size}s # %s" % [ command[:banner], command[:summary] ]
94
+ end
95
+ end
96
+ end
97
+
98
+ def help_for_command(name)
99
+ if command_alias = Transcriptic::Command.command_aliases[name]
100
+ display("Alias: #{name} redirects to #{command_alias}")
101
+ name = command_alias
102
+ end
103
+ if command = commands[name]
104
+ puts "Usage: transcriptic #{command[:banner]}"
105
+
106
+ if command[:help].strip.length > 0
107
+ puts command[:help].split("\n")[1..-1].join("\n")
108
+ end
109
+ puts
110
+ end
111
+
112
+ if commands_for_namespace(name).size > 0
113
+ puts "Additional commands, type \"transcriptic help COMMAND\" for more details:"
114
+ puts
115
+ help_for_namespace(name)
116
+ puts
117
+ elsif command.nil?
118
+ error "#{name} is not a transcriptic command. See `transcriptic help`."
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ end
@@ -0,0 +1,8 @@
1
+ module Transcriptic::Command
2
+
3
+ # show the history (audit trail) for a run
4
+ #
5
+ class History < Base
6
+ def index; end
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ # login, logout, and view your api token
2
+ #
3
+ class Transcriptic::Command::Auth < Transcriptic::Command::Base
4
+
5
+ # auth:login
6
+ #
7
+ # log in with your transcriptic account
8
+ #
9
+ def login
10
+ Transcriptic::Auth.login
11
+ display "Authentication successful."
12
+ end
13
+
14
+ alias_command "login", "auth:login"
15
+
16
+ # auth:logout
17
+ #
18
+ # clear local authentication credentials
19
+ #
20
+ def logout
21
+ Transcriptic::Auth.logout
22
+ display "Local credentials cleared."
23
+ end
24
+
25
+ alias_command "logout", "auth:logout"
26
+
27
+ # auth:token
28
+ #
29
+ # display your api token
30
+ #
31
+ def token
32
+ display Transcriptic::Auth.api_key
33
+ end
34
+
35
+ end
@@ -0,0 +1,60 @@
1
+ require 'zipruby'
2
+ require 'net/http/post/multipart'
3
+
4
+ module Transcriptic::Command
5
+
6
+ # upload and run a protocol
7
+ #
8
+ class Run < Base
9
+
10
+ # transcriptic run [FILENAME|DIRECTORY]
11
+ #
12
+ # upload FILENAME or DIRECTORY and launch it
13
+ #
14
+ def index
15
+ path = args.shift
16
+ upload_protocol(path)
17
+ end
18
+
19
+ def upload_protocol(path)
20
+ begin
21
+ stat = File.stat(path)
22
+ rescue
23
+ puts "No such path: #{path}"
24
+ return
25
+ end
26
+ if stat.directory?
27
+ files = Pathname.glob("#{path}/**/**")
28
+ file_count = files.reject(&:directory?).length
29
+ dir_count = files.reject(&:file?).length
30
+ puts "Package detected, compressing #{file_count} files (#{dir_count} directories)..."
31
+ upfile = Tempfile.new('protocol')
32
+ Zip::Archive.open(upfile.path, Zip::CREATE) do |ar|
33
+ ar.add_dir(path)
34
+ Dir.glob("#{path}/**/**").each do |path|
35
+ if File.directory?(path)
36
+ ar.add_dir(path)
37
+ else
38
+ ar.add_file(path, path)
39
+ end
40
+ end
41
+ end
42
+ else
43
+ upfile = path
44
+ end
45
+ puts "Uploading `#{path}` to Transcriptic..."
46
+ url = URI.parse('http://client.transcriptic.com/receive')
47
+ File.open(upfile) do |zf|
48
+ req = Net::HTTP::Post::Multipart.new url.path,
49
+ "file" => UploadIO.new(zf, "application/zip", "protocol.zip")
50
+ res = Net::HTTP.start(url.host, url.port) do |http|
51
+ http.request(req)
52
+ end
53
+ if res.code != 200
54
+ puts "HTTP Error: #{res.message} (#{res.code})"
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ module Transcriptic::Command
2
+
3
+ # manage sequence data
4
+ #
5
+ class Sequences < Base
6
+
7
+ # sequences
8
+ #
9
+ # list all sequence entries
10
+ #
11
+ #Examples:
12
+ #
13
+ # $ transcriptic sequences
14
+ # ...
15
+ #
16
+ def index
17
+ validate_arguments!
18
+ end
19
+
20
+ # sequences:add FILENAME.EXT
21
+ #
22
+ # upload new sequence data file. EXT must be .gb or .fasta
23
+ #
24
+ #Examples:
25
+ #
26
+ # $ transcriptic sequences:add myvector.fasta
27
+ # ...
28
+ #
29
+ def add
30
+ end
31
+
32
+ # sequences:delete NAME
33
+ #
34
+ # delete sequence by name NAME
35
+ #
36
+ #Examples:
37
+ #
38
+ # $ transcriptic sequences:delete myvector20110204
39
+ # ...
40
+ #
41
+ def remove
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ module Transcriptic::Command
2
+
3
+ # view the status of your runs
4
+ #
5
+ class Status < Base
6
+
7
+ # transcriptic status [RUNID]
8
+ #
9
+ # list active runs, or if RUNID supplied, show details of that run
10
+ #
11
+ def index
12
+ run_id = args.shift
13
+ puts run_id
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,311 @@
1
+ require "vendor/transcriptic/okjson"
2
+
3
+ module Transcriptic
4
+ module Helpers
5
+ def home_directory
6
+ running_on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
7
+ end
8
+
9
+ def running_on_windows?
10
+ RUBY_PLATFORM =~ /mswin32|mingw32/
11
+ end
12
+
13
+ def running_on_a_mac?
14
+ RUBY_PLATFORM =~ /-darwin\d/
15
+ end
16
+
17
+ def display(msg="", newline=true)
18
+ if newline
19
+ puts(msg)
20
+ else
21
+ print(msg)
22
+ STDOUT.flush
23
+ end
24
+ end
25
+
26
+ def redisplay(line, line_break = false)
27
+ display("\r\e[0K#{line}", line_break)
28
+ end
29
+
30
+ def deprecate(version)
31
+ display "!!! DEPRECATION WARNING: This command will be removed in version #{version}"
32
+ display
33
+ end
34
+
35
+ def error(msg)
36
+ STDERR.puts(msg)
37
+ exit 1
38
+ end
39
+
40
+ def confirm_billing
41
+ display
42
+ display "This action will cause your account to be billed at the end of the month"
43
+ display "For more information, see http://devcenter.heroku.com/articles/billing"
44
+ display "Are you sure you want to do this? (y/n) ", false
45
+ if ask.downcase == 'y'
46
+ heroku.confirm_billing
47
+ return true
48
+ end
49
+ end
50
+
51
+ def confirm(message = "Are you sure you wish to continue? (y/n)?")
52
+ display("#{message} ", false)
53
+ ask.downcase == 'y'
54
+ end
55
+
56
+ def confirm_command(app = app)
57
+ raise(Transcriptic::Command::CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") unless app
58
+
59
+ confirmed_app = extract_option('--confirm', false)
60
+ if confirmed_app
61
+ unless confirmed_app == app
62
+ raise(Transcriptic::Command::CommandFailed, "Confirmed app #{confirmed_app} did not match the selected app #{app}.")
63
+ end
64
+ return true
65
+ else
66
+ display
67
+ display " ! WARNING: Potentially Destructive Action"
68
+ display " ! This command will affect the app: #{app}"
69
+ display " ! To proceed, type \"#{app}\" or re-run this command with --confirm #{app}"
70
+ display
71
+ display "> ", false
72
+ if ask.downcase != app
73
+ display " ! Input did not match #{app}. Aborted."
74
+ false
75
+ else
76
+ true
77
+ end
78
+ end
79
+ end
80
+
81
+ def format_date(date)
82
+ date = Time.parse(date) if date.is_a?(String)
83
+ date.strftime("%Y-%m-%d %H:%M %Z")
84
+ end
85
+
86
+ def ask
87
+ gets.strip
88
+ end
89
+
90
+ def shell(cmd)
91
+ FileUtils.cd(Dir.pwd) {|d| return `#{cmd}`}
92
+ end
93
+
94
+ def run_command(command, args=[])
95
+ Transcriptic::Command.run(command, args)
96
+ end
97
+
98
+ def retry_on_exception(*exceptions)
99
+ retry_count = 0
100
+ begin
101
+ yield
102
+ rescue *exceptions => ex
103
+ raise ex if retry_count >= 3
104
+ sleep 3
105
+ retry_count += 1
106
+ retry
107
+ end
108
+ end
109
+
110
+ def has_git?
111
+ %x{ git --version }
112
+ $?.success?
113
+ end
114
+
115
+ def git(args)
116
+ return "" unless has_git?
117
+ flattened_args = [args].flatten.compact.join(" ")
118
+ %x{ git #{flattened_args} 2>&1 }.strip
119
+ end
120
+
121
+ def time_ago(elapsed)
122
+ if elapsed < 60
123
+ "#{elapsed.floor}s ago"
124
+ elsif elapsed < (60 * 60)
125
+ "#{(elapsed / 60).floor}m ago"
126
+ else
127
+ "#{(elapsed / 60 / 60).floor}h ago"
128
+ end
129
+ end
130
+
131
+ def truncate(text, length)
132
+ if text.size > length
133
+ text[0, length - 2] + '..'
134
+ else
135
+ text
136
+ end
137
+ end
138
+
139
+ @@kb = 1024
140
+ @@mb = 1024 * @@kb
141
+ @@gb = 1024 * @@mb
142
+ def format_bytes(amount)
143
+ amount = amount.to_i
144
+ return '(empty)' if amount == 0
145
+ return amount if amount < @@kb
146
+ return "#{(amount / @@kb).round}k" if amount < @@mb
147
+ return "#{(amount / @@mb).round}M" if amount < @@gb
148
+ return "#{(amount / @@gb).round}G"
149
+ end
150
+
151
+ def quantify(string, num)
152
+ "%d %s" % [ num, num.to_i == 1 ? string : "#{string}s" ]
153
+ end
154
+
155
+ def create_git_remote(app, remote)
156
+ return unless has_git?
157
+ return if git('remote').split("\n").include?(remote)
158
+ return unless File.exists?(".git")
159
+ git "remote add #{remote} git@#{heroku.host}:#{app}.git"
160
+ display "Git remote #{remote} added"
161
+ end
162
+
163
+ def app_urls(name)
164
+ "http://#{name}.heroku.com/ | git@heroku.com:#{name}.git"
165
+ end
166
+
167
+ def longest(items)
168
+ items.map { |i| i.to_s.length }.sort.last
169
+ end
170
+
171
+ def display_table(objects, columns, headers)
172
+ lengths = []
173
+ columns.each_with_index do |column, index|
174
+ header = headers[index]
175
+ lengths << longest([header].concat(objects.map { |o| o[column].to_s }))
176
+ end
177
+ display_row headers, lengths
178
+ display_row headers.map { |header| "-" * header.length }, lengths
179
+ objects.each do |row|
180
+ display_row columns.map { |column| row[column] }, lengths
181
+ end
182
+ end
183
+
184
+ def display_row(row, lengths)
185
+ row.zip(lengths).each do |column, length|
186
+ format = column.is_a?(Fixnum) ? "%#{length}s " : "%-#{length}s "
187
+ display format % column, false
188
+ end
189
+ display
190
+ end
191
+
192
+ def json_encode(object)
193
+ Transcriptic::OkJson.encode(object)
194
+ rescue Transcriptic::OkJson::ParserError
195
+ nil
196
+ end
197
+
198
+ def json_decode(json)
199
+ Transcriptic::OkJson.decode(json)
200
+ rescue Transcriptic::OkJson::ParserError
201
+ nil
202
+ end
203
+
204
+ def set_buffer(enable)
205
+ return unless $stdin.tty?
206
+
207
+ if enable
208
+ `stty icanon echo`
209
+ else
210
+ `stty -icanon -echo`
211
+ end
212
+ rescue
213
+ # fails on windows
214
+ end
215
+
216
+ def get_terminal_environment
217
+ { "TERM" => ENV["TERM"], "COLUMNS" => `tput cols`, "LINES" => `tput lines` }
218
+ rescue
219
+ { "TERM" => ENV["TERM"] }
220
+ end
221
+
222
+ def fail(message)
223
+ raise Transcriptic::Command::CommandFailed, message
224
+ end
225
+
226
+ ## DISPLAY HELPERS
227
+
228
+ def arrow(message)
229
+ "-----> #{message}"
230
+ end
231
+
232
+ def action(message)
233
+ display "#{arrow(message)}... ", false
234
+ Transcriptic::Helpers.enable_error_capture
235
+ yield
236
+ Transcriptic::Helpers.disable_error_capture
237
+ display "done", false
238
+ display(", #{@status}", false) if @status
239
+ display
240
+ end
241
+
242
+ def status(message)
243
+ @status = message
244
+ end
245
+
246
+ def output(message="")
247
+ return if message.to_s.strip == ""
248
+ display " " + message.split("\n").join("\n ")
249
+ end
250
+
251
+ def output_with_arrow(message="")
252
+ return if message.to_s.strip == ""
253
+ display "-----> " + message.split("\n").join("\n ")
254
+ end
255
+
256
+ def error_with_failure(message)
257
+ display "failed"
258
+ message.gsub!(/^ +! */, '')
259
+ display message.split("\n").map { |line| " ! #{line}" }.join("\n")
260
+ exit 1
261
+ end
262
+
263
+ def self.included_into
264
+ @@included_into ||= []
265
+ end
266
+
267
+ def self.extended_into
268
+ @@extended_into ||= []
269
+ end
270
+
271
+ def self.included(base)
272
+ included_into << base
273
+ end
274
+
275
+ def self.extended(base)
276
+ extended_into << base
277
+ end
278
+
279
+ def self.enable_error_capture
280
+ included_into.each do |base|
281
+ base.send(:alias_method, :error_without_failure, :error)
282
+ base.send(:alias_method, :error, :error_with_failure)
283
+ end
284
+ extended_into.each do |base|
285
+ class << base
286
+ alias_method :error_without_failure, :error
287
+ alias_method :error, :error_with_failure
288
+ end
289
+ end
290
+ end
291
+
292
+ def self.disable_error_capture
293
+ included_into.each do |base|
294
+ base.send(:alias_method, :error, :error_without_failure)
295
+ end
296
+ extended_into.each do |base|
297
+ class << base
298
+ alias_method :error, :error_without_failure
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ unless String.method_defined?(:shellescape)
306
+ class String
307
+ def shellescape
308
+ empty? ? "''" : gsub(/([^A-Za-z0-9_\-.,:\/@\n])/n, '\\\\\\1').gsub(/\n/, "'\n'")
309
+ end
310
+ end
311
+ end