cnvrg 0.0.2

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,26 @@
1
+ module Cnvrg
2
+ class Experiment
3
+ attr_reader :slug
4
+ def initialize(owner, project_slug)
5
+ @project_slug = project_slug
6
+ @owner = owner
7
+ @base_resource = "users/#{owner}/projects/#{project_slug}/"
8
+ @slug = nil
9
+ end
10
+
11
+ def start(input, platform, machine_name, start_commit,name)
12
+ res = Cnvrg::API.request(@base_resource + "experiment/start", 'POST', { input: input, platform: platform, machine_name: machine_name, start_commit: start_commit , title:name})
13
+ Cnvrg::CLI.is_response_success(res)
14
+
15
+ @slug = res.to_h["result"].to_h["slug"]
16
+
17
+ return res
18
+
19
+ end
20
+
21
+ def end(output, exit_status, end_commit,cpu_average, memory_average)
22
+ response = Cnvrg::API.request(@base_resource + "experiment/end", 'POST', { output: output, exp_slug: @slug, exit_status: exit_status, end_commit: end_commit, cpu_average:cpu_average,memory_average:memory_average })
23
+ Cnvrg::CLI.is_response_success(response)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,115 @@
1
+ require 'mimemagic'
2
+ require 'pry'
3
+ module Cnvrg
4
+ class Files
5
+
6
+ attr_reader :base_resource
7
+
8
+ def initialize(owner, project_slug)
9
+ @project_slug = project_slug
10
+ @owner = owner
11
+ @base_resource = "users/#{owner}/projects/#{project_slug}/"
12
+ end
13
+
14
+ def upload_file(absolute_path, relative_path, commit_sha1)
15
+ file_name = File.basename relative_path
16
+ file_size = File.size(relative_path).to_f
17
+ upload_resp = Cnvrg::API.request(@base_resource + "upload_file", 'POST_FILE', { absolute_path: absolute_path, relative_path: relative_path,
18
+ commit_sha1: commit_sha1, file_name: file_name ,file_size:file_size})
19
+ if Cnvrg::CLI.is_response_success(upload_resp, false)
20
+ path = upload_resp["result"]["path"]
21
+ s3_res = upload_s3(path,relative_path)
22
+ if s3_res
23
+ Cnvrg::API.request(@base_resource + "update_s3", 'POST', { path:path,commit_id:upload_resp["result"]["commit_id"],
24
+ blob_id:upload_resp["result"]["blob_id"]})
25
+ return true
26
+ end
27
+ end
28
+ return false
29
+ end
30
+ def upload_s3(url,file)
31
+ url = URI.parse(url)
32
+ mime_type = MimeMagic.by_path(file)
33
+ content_type = !mime_type.nil? ? mime_type.type : ""
34
+ file = File.open(file, "rb")
35
+ body = file.read
36
+ begin
37
+ Net::HTTP.start(url.host) do |http|
38
+ http.send_request("PUT", url.request_uri, body, {
39
+ "content-type" => content_type,
40
+ })
41
+ end
42
+ return true
43
+ rescue Interrupt
44
+ return false
45
+ rescue => e
46
+ return false
47
+ end
48
+ end
49
+ def upload_url(file_path)
50
+ response = Cnvrg::API.request(@base_resource + "upload_url", 'POST', { file_s3_path: file_path})
51
+ if Cnvrg::CLI.is_response_success(response, false)
52
+ return response
53
+ else
54
+ return nil
55
+ end
56
+
57
+ end
58
+ def delete_file(absolute_path, relative_path, commit_sha1)
59
+ response = Cnvrg::API.request(@base_resource + "delete_file", 'DELETE',{ absolute_path: absolute_path, relative_path: relative_path, commit_sha1: commit_sha1 })
60
+ return Cnvrg::CLI.is_response_success(response, false)
61
+ end
62
+
63
+ def delete_dir(absolute_path, relative_path, commit_sha1)
64
+ response = Cnvrg::API.request(@base_resource + "delete_dir", 'DELETE', { absolute_path: absolute_path, relative_path: relative_path, commit_sha1: commit_sha1 })
65
+ return Cnvrg::CLI.is_response_success(response, false)
66
+ end
67
+
68
+ def create_dir(absolute_path, relative_path, commit_sha1)
69
+ response = Cnvrg::API.request(@base_resource + "create_dir", 'POST',{ absolute_path: absolute_path, relative_path: relative_path, commit_sha1: commit_sha1 })
70
+ return Cnvrg::CLI.is_response_success(response, false)
71
+ end
72
+
73
+ def download_file(absolute_path, relative_path, project_home, conflict=false)
74
+ res = Cnvrg::API.request(@base_resource + "download_file", 'POST', { absolute_path: absolute_path, relative_path: relative_path })
75
+ Cnvrg::CLI.is_response_success(res, false)
76
+ if res["result"]
77
+ res = res["result"]
78
+ return false if res["link"].empty? or res["filename"].empty?
79
+ filename = res["filename"]
80
+ file_location = absolute_path.gsub(/#{filename}\/?$/, "")
81
+
82
+ FileUtils.mkdir_p project_home + "/" + file_location
83
+ filename += ".conflict" if conflict
84
+
85
+ File.open("#{project_home}/#{file_location}/#{filename}", "wb") do |file|
86
+ file.write open(res["link"]).read
87
+ end
88
+ else
89
+ return false
90
+ end
91
+ return true
92
+ end
93
+
94
+ def download_dir(absolute_path, relative_path, project_home)
95
+ FileUtils.mkdir_p("#{project_home}/#{absolute_path}")
96
+ end
97
+
98
+ def start_commit
99
+
100
+ response = Cnvrg::API.request("#{base_resource}/commit/start", 'POST', { project_slug: @project_slug,
101
+ username: @owner} )
102
+ Cnvrg::CLI.is_response_success(response)
103
+ return response
104
+ end
105
+
106
+ def end_commit(commit_sha1)
107
+ response = Cnvrg::API.request("#{base_resource}/commit/end", 'POST', { commit_sha1: commit_sha1 } )
108
+ return response
109
+ end
110
+ def rollback_commit(commit_sha1)
111
+ response = Cnvrg::API.request("#{base_resource}/commit/rollback", 'POST', { commit_sha1: commit_sha1 } )
112
+ Cnvrg::CLI.is_response_success(response, false)
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,90 @@
1
+ module Cnvrg
2
+ module Helpers
3
+
4
+ extend self
5
+ def checkmark
6
+ checkmark = "\u2713"
7
+ return checkmark.encode('utf-8')
8
+ end
9
+
10
+ def remote_url
11
+ "https://cnvrg.io"
12
+ end
13
+
14
+ def windows?
15
+ !!(RUBY_PLATFORM =~ /mswin32|mingw32/)
16
+ end
17
+
18
+ def mac?
19
+ !!(RUBY_PLATFORM =~ /-darwin\d/)
20
+ end
21
+
22
+ def linux?
23
+ not mac? and not windows?
24
+ end
25
+
26
+ def cnvrgignore_content
27
+ %{
28
+ # cnvrg ignore: Ignore the following directories and files
29
+ # for example:
30
+ # some_dir/
31
+ # some_file.txt
32
+ }.strip
33
+ end
34
+
35
+ def readme_content
36
+ %{
37
+ # README
38
+
39
+ This README would normally contain some context and description about the project.
40
+
41
+ Things you may want to cover:
42
+
43
+ * Data description
44
+
45
+ * Benchmark and measurement guidelines
46
+
47
+ * Used algorithms
48
+
49
+ * Scores
50
+
51
+ * Configurations
52
+
53
+ * Requirements
54
+
55
+ * How to run the experiments
56
+
57
+ * ...}.strip
58
+ end
59
+
60
+ def netrc_domain
61
+ "cnvrg.io"
62
+ end
63
+
64
+ def look_for_in_path(path, name)
65
+ url_split = path.split("/")
66
+ url_split.each_with_index do |u, i|
67
+ if u == name
68
+ return i
69
+ end
70
+ end
71
+ return -1
72
+ end
73
+
74
+ # cpu
75
+
76
+ def cpu_time
77
+ Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :microsecond)
78
+ end
79
+
80
+ def wall_time
81
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
82
+ end
83
+
84
+ # memory
85
+ #
86
+ def get_mem(pid)
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'commander/import'
5
+ require "open4"
6
+ require 'netrc'
7
+ require 'net/http'
8
+ require 'uri'
9
+ require 'json'
10
+ require 'yaml'
11
+ require 'digest' # sha1
12
+
13
+ # DEV VERSION
14
+ #require 'pry'
15
+ #
16
+
17
+
18
+ program :name, 'cnvrg'
19
+ program :version, '0.0.1'
20
+ program :description, 'cnvrg\'s cli for running scripts'
21
+
22
+ command :idx do |c|
23
+ c.action do |args,options|
24
+ generate_idx
25
+ end
26
+ end
27
+ command :status do |c|
28
+ c.action do |args,options|
29
+ status
30
+ end
31
+ end
32
+ command :run do |c|
33
+ c.syntax = 'cnvrg run script [options]'
34
+ c.summary = 'monitor run of script'
35
+ c.description = 'Runs scripts and sending notification when finished'
36
+ c.option '--script_args script arguments', String, 'script arguments if exist'
37
+ c.option '--sms', String, 'set sms if you want to be notify by sms when the run is over'
38
+ c.option '--email', String, 'set email if you want to be notify by email when the run is over'
39
+ c.option '--silent', String, 'set silent if you want to run script in non-blocking'
40
+ c.action do |args, options|
41
+ is_exist = verify_auth_exist()
42
+ unless is_exist
43
+ puts("You have to be logged in to continue. Please login.\n")
44
+ loged_in = login_process()
45
+ if !loged_in
46
+ puts("Error: Couldn't logged you in, please run cnvr login separately")
47
+ end
48
+ end
49
+
50
+ silent = ''
51
+ if options.silent
52
+ silent = '&'
53
+ end
54
+ script = args[0]
55
+ if args.length != 1
56
+ puts "Error: only one script should be listed to run"
57
+ found = false
58
+ args.each do |arg|
59
+ ans = agree("should I run #{arg}?")
60
+ if ans
61
+ found = true
62
+ script = arg
63
+ break
64
+ end
65
+ end
66
+ if not found
67
+ exit(0)
68
+ end
69
+ end
70
+ #check if file exist
71
+ exist = File.file?(script)
72
+ if not exist
73
+ puts "Error: Can't find #{script}, try using the full path"
74
+ exit(0)
75
+ end
76
+ puts "Running #{script} with args: #{options.script_args}"
77
+ body = {try: 'yay'}.to_json
78
+ puts run(true,body)
79
+ pid, stdin, stdout, stderr = Open4::popen4 "sh"
80
+ stdin.puts "./#{script} #{options.script_args}"
81
+ stdin.close
82
+
83
+ ignored, status = Process::waitpid2 pid
84
+
85
+ #try to run
86
+ if stderr.read.strip.include? "Permission denied"
87
+ ans = agree("Your script #{script} isn't executable, should I make it executable?")
88
+ if ans
89
+ puts "Making #{script} executable"
90
+ run_result = %x(chmod +x ./#{script} 2>&1)
91
+ if run_result.include? "Operation not permitted"
92
+ puts "Can't make script executable,you should first run sudo chmod +x #{script}"
93
+ exit(0)
94
+ end
95
+ puts "Running again #{script} with args: #{options.script_args}"
96
+
97
+ pid, stdin, stdout, stderr = Open4::popen4 "sh"
98
+ stdin.puts "./#{script} #{options.script_args}"
99
+ stdin.close
100
+ ignored, status = Process::waitpid2 pid
101
+
102
+ if !stderr.read.strip.empty?
103
+ puts "Error: #{stderr.read.strip}"
104
+ end
105
+ end
106
+ end
107
+ puts stdout.read.strip
108
+ # send results to the server
109
+ puts "Finished running with status #{status.exitstatus}"
110
+ puts run(false, body)
111
+
112
+ end
113
+ end
114
+
115
+ command :new do |c|
116
+ c.syntax = "cnvrg new project-name"
117
+ c.summary = "Create a new data science project"
118
+
119
+ c.action do |args, options|
120
+ is_exist = verify_auth_exist()
121
+ unless is_exist
122
+ "You have to login"
123
+ else
124
+ if args[0].to_s.size == 0
125
+ exit(0)
126
+ else
127
+ new(args[0])
128
+ link_dir(args[0], "")
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ command :login do |c|
135
+ c.syntax = 'cnvrg login'
136
+ c.summary = 'login to cnvrg app'
137
+ c.description = 'Login to the CNVRG system'
138
+ c.option '--reset', String, 'set reset if you want to change current login credentials'
139
+ c.action do |args, options|
140
+ n = Netrc.read
141
+ user, pass = n["cnvrg.io"]
142
+ if user and pass
143
+ relogin = agree("You are already authenticated with email: #{user}, do you want to login again? (This will delete you're current login credentials")
144
+ if !relogin
145
+ exit(0)
146
+ end
147
+ end
148
+ if options.reset
149
+ n["cnvrg.io"] = "", ""
150
+
151
+ end
152
+ login_process()
153
+
154
+
155
+ end
156
+ end
157
+
158
+ def login_process()
159
+ n = Netrc.read
160
+
161
+ email = ask("Please enter your email")
162
+ email_regex = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
163
+ is_valid = email_regex.match(email)
164
+ while !is_valid do
165
+ email = ask("Please enter a valid email address:")
166
+ is_valid = email_regex.match(email)
167
+ end
168
+
169
+ password = ask("Please enter your password: ") { |q| q.echo = false }
170
+ #send API to login
171
+ token = sign_in(email, password)
172
+ count = 0
173
+ while token.nil? and count<3 do
174
+ do_retry = agree("Wrong email or password, try again?")
175
+ if do_retry
176
+ count +=1
177
+ email = ask("Please enter your email")
178
+ password = ask("Please enter your password: ") { |q| q.echo = false }
179
+ token = sign_in(email, password)
180
+ end
181
+ end
182
+ if token.nil?
183
+ puts "Error: Too many sign in retries"
184
+ exit(0)
185
+ else
186
+
187
+
188
+ n.new_item_prefix = "# This entry was added automatically by CNVRG\n"
189
+ n["cnvrg.io"] = email, token
190
+ n.save
191
+ puts "Great, you're in."
192
+ return true
193
+ end
194
+ end
195
+
196
+ def sign_in(email, password)
197
+ url = URI.parse("http://localhost:3000/api/v1/users/sign_in")
198
+
199
+ req = Net::HTTP::Post.new(url.path)
200
+ req.add_field("EMAIL", email)
201
+ req.add_field("PASSWORD", password)
202
+
203
+ response = Net::HTTP.new(url.host, url.port).start do |http|
204
+ http.request(req)
205
+ end
206
+
207
+ result = JSON.parse(response.body)
208
+ if result["status"] == 200
209
+ return result["token"]
210
+ else
211
+ return nil
212
+ end
213
+ end
214
+
215
+ def verify_auth_exist
216
+ n = Netrc.read
217
+ user, pass = n["cnvrg.io"]
218
+ if user.nil? or pass.nil?
219
+ puts "Please login first, using \"cnvrg login\" command"
220
+ return false
221
+ end
222
+ return true
223
+ end
224
+
225
+ def get_logged_token
226
+ n = Netrc.read
227
+ user, token = n["cnvrg.io"]
228
+ return token
229
+
230
+ end
231
+ def run(is_start, body)
232
+ token = get_logged_token()
233
+ if is_start
234
+ url = URI.parse("http://localhost:3000/api/v1/cli/start_run")
235
+ else
236
+ url = URI.parse("http://localhost:3000/api/v1/cli/end_run")
237
+ end
238
+
239
+
240
+ req = Net::HTTP::Post.new(url.path)
241
+ req.add_field("AUTH_TOKEN", token)
242
+ req.body = body
243
+ response = Net::HTTP.new(url.host, url.port).start do |http|
244
+ http.request(req)
245
+ end
246
+
247
+ result = JSON.parse(response.body)
248
+ puts result
249
+ end
250
+
251
+ def new(project_name)
252
+ begin
253
+ `mkdir -p #{project_name} #{project_name}/data #{project_name}/models #{project_name}/notebooks #{project_name}/src #{project_name}/src/data #{project_name}/src/features #{project_name}/src/models #{project_name}/src/visualizations #{project_name}/.cnvrg`
254
+ `touch #{project_name}/README.md`
255
+ `touch #{project_name}/.cnvrgignore`
256
+ rescue
257
+ "Error"
258
+ end
259
+ end
260
+
261
+ def link_dir(project_name, user, host="cnvrg.io")
262
+ `touch #{project_name}/.cnvrg/project`
263
+ `echo "owner: #{user}\nproject_slug: #{project_name}\nhost: #{host}" >> #{project_name}/.cnvrg/project`
264
+ end
265
+
266
+ def download
267
+
268
+ status_output = status
269
+ downloadables = status_output["updated_on_server"].merge(status_output["deleted"])
270
+
271
+
272
+ downloadables.each do |d|
273
+ if d[-1] == "/"
274
+ # make dir
275
+ else
276
+ files << #download(d)
277
+ end
278
+ end
279
+
280
+ def upload
281
+
282
+ end
283
+
284
+ def generate_idx
285
+
286
+ idx = Hash.new(0)
287
+ list = Dir['**/**']
288
+
289
+ # remove files that are in CNVRGIGNORE
290
+ # list.select { |x| ! cnvrgignore.include? x }
291
+
292
+ list.each do |e|
293
+ if File.directory? e
294
+ idx[e+"/"] = nil
295
+ else
296
+ idx[e] = { sha1: Digest::SHA1.hexdigest(File.read(e)),
297
+ commit_time: nil }
298
+ end
299
+ end
300
+
301
+ File.open("hello/.cnvrg/idx.yml", 'w') { |f| f.write idx.to_yaml }
302
+
303
+
304
+ end
305
+
306
+ def status
307
+ url = URI.parse("http://localhost:3000/api/v1/users/yochze/projects/porject/status")
308
+ token = get_logged_token()
309
+ load_idx = YAML.load(File.read("hello/.cnvrg/idx.yml"))
310
+ req = Net::HTTP::Get.new(url, 'Content-Type' => 'application/json')
311
+ req.add_field("AUTH_TOKEN", token)
312
+ req.body = { snapshot: load_idx }.to_json
313
+ response = Net::HTTP.new(url.host, url.port).start do |http|
314
+ http.request(req)
315
+ end
316
+
317
+ result = JSON.parse(response.body)
318
+
319
+ result["status"]["added"].each do |a|
320
+ puts "A:\t#{a}"
321
+ end
322
+
323
+ result["status"]["updated_on_server"].each do |a|
324
+ puts "M:\t#{a}"
325
+ end
326
+
327
+ result["status"]["deleted"].each do |a|
328
+ puts "D:\t#{a}"
329
+ end
330
+
331
+ return result["status"]
332
+ end