transcriptic 0.1.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.
@@ -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