mobilize-base 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -75,7 +75,7 @@ module GoogleDrive
75
75
  def add_worker_acl
76
76
  f = self
77
77
  return true if f.has_worker_acl?
78
- Mobilize::Gdriver.worker_emails.each do |a|
78
+ Mobilize::Gdrive.worker_emails.each do |a|
79
79
  f.update_acl(a)
80
80
  end
81
81
  end
@@ -84,7 +84,7 @@ module GoogleDrive
84
84
  f = self
85
85
  #admin includes workers
86
86
  return true if f.has_admin_acl?
87
- (Mobilize::Gdriver.admin_emails + Mobilize::Gdriver.worker_emails).each do |a|
87
+ (Mobilize::Gdrive.admin_emails + Mobilize::Gdrive.worker_emails).each do |a|
88
88
  f.update_acl(a)
89
89
  end
90
90
  end
@@ -92,7 +92,7 @@ module GoogleDrive
92
92
  def has_admin_acl?
93
93
  f = self
94
94
  curr_emails = f.acls.map{|a| a.scope}.sort
95
- admin_emails = Mobilize::Gdriver.admin_emails.sort
95
+ admin_emails = Mobilize::Gdrive.admin_emails.sort
96
96
  if (curr_emails & admin_emails) == admin_emails
97
97
  return true
98
98
  else
@@ -103,7 +103,7 @@ module GoogleDrive
103
103
  def has_worker_acl?
104
104
  f = self
105
105
  curr_emails = f.acls.map{|a| a.scope}.sort
106
- worker_emails = Mobilize::Gdriver.worker_emails.sort
106
+ worker_emails = Mobilize::Gdrive.worker_emails.sort
107
107
  if (curr_emails & worker_emails) == worker_emails
108
108
  return true
109
109
  else
@@ -159,6 +159,32 @@ module GoogleDrive
159
159
  row_last_i = (header.index("") || header.length)-1
160
160
  rows.map{|r| r[0..row_last_i]}.map{|r| r.join("\t")}.join("\n")
161
161
  end
162
+ def add_headers(headers)
163
+ headers.each_with_index do |h,h_i|
164
+ self[1,h_i+1] = h
165
+ end
166
+ end
167
+
168
+ def add_or_update_rows(upd_rows)
169
+ sheet = self
170
+ curr_rows = sheet.to_tsv.tsv_to_hash_array
171
+ headers = curr_rows.first.keys
172
+ curr_rows = [] if curr_rows.length==1 and curr_rows.first['name'].nil?
173
+ curr_row_names = curr_rows.map{|r| r['name']}
174
+ upd_rows.each_with_index do |row,urow_i|
175
+ crow_i = curr_row_names.index(row['name'])
176
+ if crow_i.nil?
177
+ curr_row_names << row['name']
178
+ crow_i = curr_row_names.length-1
179
+ end
180
+ row.each do |col_n,col_v|
181
+ col_v_i = headers.index(col_n)
182
+ sheet[crow_i+2,col_v_i+1] = col_v
183
+ end
184
+ end
185
+ sheet.save
186
+ end
187
+
162
188
  def write(tsv,check=true,job_id=nil)
163
189
  sheet = self
164
190
  tsvrows = tsv.split("\n")
@@ -197,7 +223,6 @@ module GoogleDrive
197
223
  if job_id
198
224
  newstatus = "100 pct written at #{Time.now.utc}"
199
225
  Mobilize::Job.find(job_id).update_status(newstatus)
200
- newstatus.oputs
201
226
  end
202
227
  break
203
228
  else
@@ -13,6 +13,7 @@ class String
13
13
  end
14
14
  def bash(except=true)
15
15
  pid,stdin,stdout,stderr = Open4.popen4(self)
16
+ pid,stdin = [nil,nil]
16
17
  raise stderr.read if (stderr.read.length>0 and except==true)
17
18
  return stdout.read
18
19
  end
@@ -70,6 +71,7 @@ class String
70
71
  begin
71
72
  return JSON.parse(self)
72
73
  rescue => exc
74
+ exc = nil
73
75
  return {}
74
76
  end
75
77
  end
@@ -80,7 +82,7 @@ class String
80
82
  headers = rows.first.split("\t")
81
83
  if rows.length==1
82
84
  #return single member hash with all nil values
83
- return [headers.map{|k| {k=>nil}}.inject{|k,h| h.merge(k)}]
85
+ return [headers.map{|k| {k=>nil}}.inject{|k,h| k.merge(h)}]
84
86
  end
85
87
  row_hash_arr =[]
86
88
  rows[1..-1].each do |row|
@@ -1,21 +1,21 @@
1
1
  module Mobilize
2
2
  require 'action_mailer'
3
- class Emailer < ActionMailer::Base
3
+ class Email < ActionMailer::Base
4
4
  ActionMailer::Base.delivery_method = :smtp
5
5
 
6
6
  ActionMailer::Base.smtp_settings = {
7
7
  :address => "smtp.gmail.com",
8
8
  :port => 587,
9
- :domain => 'ngmoco.com',
10
- :user_name => Gdriver.owner_email,
11
- :password => Gdriver.password(Gdriver.owner_email),
9
+ :domain => Gdrive.domain,
10
+ :user_name => Gdrive.owner_email,
11
+ :password => Gdrive.password(Gdrive.owner_email),
12
12
  :authentication => 'plain',
13
13
  :enable_starttls_auto => true }
14
14
 
15
15
  def write(subj,
16
16
  bod="",
17
17
  recipient=Jobtracker.admin_emails.join(","))
18
- mail(:from=>Gdriver.owner_email,
18
+ mail(:from=>Gdrive.owner_email,
19
19
  :to=>recipient,
20
20
  :subject=>subj,
21
21
  :body=>bod)
@@ -0,0 +1,73 @@
1
+ module Mobilize
2
+ module Gbook
3
+ def Gbook.find_all_by_title(title,email=nil)
4
+ Gdrive.books(email,{"title"=>title,"title-exact"=>"true"})
5
+ end
6
+ def Gbook.find_or_create_by_title(title,email)
7
+ books = Gdrive.books(email,{"title"=>title,"title-exact"=>"true"})
8
+ #there should only be one book with each title, otherwise we have fail
9
+ book = nil
10
+ if books.length>1
11
+ #some idiot process created a duplicate book.
12
+ #Fix by renaming all but one with dst entry's key
13
+ dst = Dataset.find_by_handler_and_name('gbook',title)
14
+ dkey = dst.url.split("key=").last
15
+ books.each do |b|
16
+ bkey = b.resource_id.split(":").last
17
+ if bkey == dkey
18
+ book = b
19
+ else
20
+ #delete the invalid book
21
+ b.delete
22
+ ("Deleted duplicate book #{title}").oputs
23
+ end
24
+ end
25
+ else
26
+ book = books.first
27
+ end
28
+ if book.nil?
29
+ #add book using owner email
30
+ #http
31
+ book = Gdrive.root.create_spreadsheet(title)
32
+ ("Created book #{title} at #{Time.now.utc.to_s}").oputs
33
+ end
34
+ #delete Sheet1 if there are other sheets
35
+ #http
36
+ if (sheets = book.worksheets).length>1
37
+ sheet1 = sheets.select{|s| s.title == "Sheet1"}.first
38
+ #http
39
+ sheet1.delete if sheet1
40
+ end
41
+ #always make sure books have admin acl
42
+ book.add_admin_acl
43
+ return book
44
+ end
45
+
46
+ def Gbook.find_or_create_by_dst_id(dst_id,email=nil)
47
+ #creates by title, updates acl, updates dataset with url
48
+ dst = Dataset.find(dst_id)
49
+ r = Requestor.find(dst.requestor_id)
50
+ book = nil
51
+ #http
52
+ book = Gdrive.root.spreadsheet_by_url(dst.url) if dst.url
53
+ #manually try 5 times to validate sheet since we can't just try again and again
54
+ 5.times.each do
55
+ begin
56
+ book.resource_id
57
+ #if no error then break loop
58
+ break
59
+ rescue=>exc
60
+ if book.nil? or exc.to_s.index('Invalid document id')
61
+ book = Gbook.find_or_create_by_title(dst.name,email)
62
+ #if invalid doc then update url w new book and break loop
63
+ dst.update_attributes(:url=>book.human_url)
64
+ break
65
+ end
66
+ end
67
+ end
68
+ #add requestor write access
69
+ book.update_acl(r.email)
70
+ return book
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,72 @@
1
+ module Mobilize
2
+ module Gdrive
3
+ def Gdrive.config
4
+ Base.config('gdrive')[Base.env]
5
+ end
6
+
7
+ def Gdrive.domain
8
+ Gdrive.config['domain']
9
+ end
10
+
11
+ def Gdrive.owner_email
12
+ Gdrive.config['owner']['email']
13
+ end
14
+
15
+ def Gdrive.password(email)
16
+ if email == Gdrive.owner_email
17
+ Gdrive.config['owner']['pw']
18
+ else
19
+ worker = Gdrive.workers(email)
20
+ return worker['pw'] if worker
21
+ end
22
+ end
23
+
24
+ def Gdrive.admins
25
+ Gdrive.config['admins']
26
+ end
27
+
28
+ def Gdrive.workers(email=nil)
29
+ if email.nil?
30
+ Gdrive.config['workers']
31
+ else
32
+ Gdrive.workers.select{|w| w['email'] == email}.first
33
+ end
34
+ end
35
+
36
+ def Gdrive.worker_emails
37
+ Gdrive.workers.map{|w| w['email']}
38
+ end
39
+
40
+ def Gdrive.admin_emails
41
+ Gdrive.admins.map{|w| w['email']}
42
+ end
43
+
44
+ #email management - used to make sure not too many emails get used at the same time
45
+ def Gdrive.get_worker_email_by_mongo_id(mongo_id)
46
+ active_emails = Mobilize::Resque.jobs('working').map{|j| j['email'] if j['email']}.compact
47
+ Gdrive.workers.sort_by{rand}.each do |w|
48
+ if !(active_emails.include?(w['email']))
49
+ Mobilize::Resque.update_job_email(mongo_id,w['email'])
50
+ return w['email']
51
+ end
52
+ end
53
+ #return false if none are available
54
+ return false
55
+ end
56
+
57
+ def Gdrive.root(email=nil)
58
+ email ||= Gdrive.owner_email
59
+ pw = Gdrive.password(email)
60
+ GoogleDrive.login(email,pw)
61
+ end
62
+
63
+ def Gdrive.files(email=nil,params={})
64
+ root = Gdrive.root(email)
65
+ root.files(params)
66
+ end
67
+
68
+ def Gdrive.books(email=nil,params={})
69
+ Gdrive.files(email,params).select{|f| f.class==GoogleDrive::Spreadsheet}
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,55 @@
1
+ module Mobilize
2
+ class Gfile
3
+ def Gfile.find_by_title(title,email=nil)
4
+ Gdriver.files(email).select{|f| f.title==title}.first
5
+ end
6
+
7
+ def Gfile.find_by_dst_id(dst_id,email=nil)
8
+ dst = Dataset.find(dst_id)
9
+ Gfile.find_by_title(dst.path,email)
10
+ end
11
+
12
+ def Gfile.add_admin_acl_by_dst_id(dst_id)
13
+ #adds admins and workers as writers
14
+ file = Gfile.find_by_dst_id(dst_id)
15
+ file.add_admin_acl
16
+ return true
17
+ end
18
+
19
+ def Gfile.add_admin_acl_by_title(title)
20
+ file = Gfile.find_by_title(title)
21
+ file.add_admin_acl
22
+ return true
23
+ end
24
+
25
+ def Gfile.add_worker_acl_by_title(title)
26
+ file = Gfile.find_by_title(title)
27
+ file.add_worker_acl
28
+ return true
29
+ end
30
+
31
+ def Gfile.update_acl_by_dst_id(dst_id,email,role="writer",edit_email=nil)
32
+ dst = Dataset.find(dst_id)
33
+ Gfile.update_acl_by_title(dst.path,email,role,edit_email)
34
+ end
35
+
36
+ def Gfile.update_acl_by_title(title,email,role="writer",edit_email=nil)
37
+ file = Gfile.find_by_title(title,edit_email)
38
+ raise "File #{title} not found" unless file
39
+ file.update_acl(email,role)
40
+ end
41
+
42
+ def Gfile.read_by_name()
43
+
44
+ end
45
+
46
+ def Gfile.read_by_url()
47
+
48
+ end
49
+
50
+ def Gfile.read_by_job_id(job_id)
51
+ j = Job.find(job_id)
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,123 @@
1
+ module Mobilize
2
+ module Gsheet
3
+
4
+ def Gsheet.max_cells
5
+ 400000
6
+ end
7
+
8
+ def Gsheet.read(name,email=nil)
9
+ sheet = Gsheet.find_or_create_by_name(name,email)
10
+ sheet.to_tsv
11
+ end
12
+
13
+ def Gsheet.write(name,tsv,email=nil)
14
+ sheet = Gsheet.find_or_create_by_name(name,email)
15
+ sheet.write(tsv)
16
+ end
17
+
18
+ def Gsheet.find_all_by_name(name,email)
19
+ book_title,sheet_title = name.split("/")
20
+ books = Gdrive.books(email,{"title"=>book_title,"title-exact"=>"true"})
21
+ sheets = books.map{|b| b.worksheets}.flatten.select{|w| w.title == sheet_title }
22
+ sheets
23
+ end
24
+
25
+ def Gsheet.find_or_create_by_name(name,email=nil,rows=100,cols=20)
26
+ book_title,sheet_title = name.split("/")
27
+ book = Gbook.find_or_create_by_title(book_title,email)
28
+ #http
29
+ sheet = book.worksheets.select{|w| w.title==sheet_title}.first
30
+ if sheet.nil?
31
+ #http
32
+ sheet = book.add_worksheet(sheet_title,rows,cols)
33
+ ("Created sheet #{name} at #{Time.now.utc.to_s}").oputs
34
+ end
35
+ return sheet
36
+ end
37
+
38
+ def Gsheet.find_or_create_by_dst_id(dst_id,email=nil)
39
+ #creates by title, updates acl, updates dataset with url
40
+ dst = Dataset.find(dst_id)
41
+ r = Requestor.find(dst.requestor_id)
42
+ name = dst.name
43
+ book_title,sheet_title = name.split("/")
44
+ #make sure book exists and is assigned to this user
45
+ r.find_or_create_gbook_by_title(book_title,email)
46
+ #add admin write access
47
+ sheet = Gsheet.find_or_create_by_name(name)
48
+ sheet_title = nil
49
+ return sheet
50
+ end
51
+
52
+ def Gsheet.read_by_dst_id(dst_id,email=nil)
53
+ dst = Dataset.find(dst_id)
54
+ name = dst.name
55
+ sheet = Gsheet.find_or_create_by_name(name,email)
56
+ output = sheet.to_tsv
57
+ return output
58
+ end
59
+
60
+ def Gsheet.read_by_job_id(job_id)
61
+ j = Job.find(job_id)
62
+ #reserve email account for read
63
+ email = Gdrive.get_worker_email_by_mongo_id(job_id)
64
+ return false unless email
65
+ #pull tsv from cache
66
+ j.dataset_array.first.read_cache
67
+ end
68
+
69
+ def Gsheet.write_by_dst_id(dst_id,tsv,email=nil)
70
+ dst = Dataset.find(dst_id)
71
+ #see if this is a specific cell
72
+ name = dst.name
73
+ return false unless email
74
+ #create temp tab, write data to it, checksum it against the source
75
+ temp_sheet = Gsheet.find_or_create_by_name("#{name}_temp")
76
+ temp_sheet.write(tsv)
77
+ #delete current sheet, replace it with temp one
78
+ sheet = Gsheet.find_or_create_by_name(dst.name)
79
+ title = sheet.title
80
+ #http
81
+ sheet.delete
82
+ begin
83
+ temp_sheet.rename(title)
84
+ rescue
85
+ #need this because sometimes it gets confused and tries to rename twice
86
+ end
87
+ "Write successful for #{write_name}".oputs
88
+ return true
89
+ end
90
+
91
+ def Gsheet.write_by_job_id(job_id)
92
+ j = Job.find(job_id)
93
+ r = j.requestor
94
+ dest_name = if j.destination.split("/").length==1
95
+ "#{r.jobspec_title}#{"/"}#{j.destination}"
96
+ else
97
+ j.destination
98
+ end
99
+ sheet_dst = Dataset.find_or_create_by_handler_and_name('gsheet',dest_name)
100
+ sheet_dst.update_attributes(:requestor_id=>r.id.to_s) if sheet_dst.requestor_id.nil?
101
+ email = Gdrive.get_worker_email_by_mongo_id(job_id)
102
+ #return false if there are no emails available
103
+ return false unless email
104
+ #create temp tab, write data to it, checksum it against the source
105
+ temp_sheet_dst = Dataset.find_or_create_by_handler_and_name('gsheet',"#{dest_name}_temp")
106
+ temp_sheet_dst.update_attributes(:requestor_id=>r.id.to_s) if temp_sheet_dst.requestor_id.nil?
107
+ temp_sheet = Gsheet.find_or_create_by_name(temp_sheet_dst.name,email)
108
+ #tsv is the prior task's output
109
+ tsv = j.task_output_dsts[j.task_idx-1].read
110
+ temp_sheet.write(tsv,true,job_id)
111
+ #delete current sheet, replace it with temp one
112
+ sheet = Gsheet.find_or_create_by_name(dest_name,email)
113
+ title = sheet.title
114
+ #http
115
+ sheet.delete
116
+ temp_sheet.title = title
117
+ temp_sheet.save
118
+ sheet_dst.update_attributes(:url=>temp_sheet.spreadsheet.human_url)
119
+ "Write successful for #{dest_name}".oputs
120
+ return true
121
+ end
122
+ end
123
+ end
@@ -1,16 +1,16 @@
1
1
  module Mobilize
2
- class Mongoer
2
+ class Mongodb
3
3
 
4
- def Mongoer.grid
4
+ def Mongodb.grid
5
5
  session = ::Mongoid.configure.sessions['default']
6
6
  database_name = session['database']
7
7
  host,port = session['hosts'].first.split(":")
8
8
  return ::Mongo::GridFileSystem.new(::Mongo::Connection.new(host,port).db(database_name))
9
9
  end
10
10
 
11
- def Mongoer.read_by_filename(filename)
11
+ def Mongodb.read_by_filename(filename)
12
12
  begin
13
- zs=Mongoer.grid.open(filename,'r').read
13
+ zs=Mongodb.grid.open(filename,'r').read
14
14
  return ::Zlib::Inflate.inflate(zs)
15
15
  rescue
16
16
  "failed Mongo read for filename #{filename}".oputs
@@ -18,14 +18,14 @@ module Mobilize
18
18
  end
19
19
  end
20
20
 
21
- def Mongoer.write_by_filename(filename,string)
21
+ def Mongodb.write_by_filename(filename,string)
22
22
  zs = ::Zlib::Deflate.deflate(string)
23
- Mongoer.grid.open(filename,'w',:delete_old => true){|f| f.write(zs)}
23
+ Mongodb.grid.open(filename,'w',:delete_old => true){|f| f.write(zs)}
24
24
  return true
25
25
  end
26
26
 
27
- def Mongoer.delete_by_filename(filename)
28
- Mongoer.grid.delete(filename)
27
+ def Mongodb.delete_by_filename(filename)
28
+ Mongodb.grid.delete(filename)
29
29
  return true
30
30
  end
31
31
  end
@@ -154,13 +154,6 @@ module Mobilize
154
154
  Resque.kill_workers(count)
155
155
  end
156
156
 
157
- def Jobtracker.set_test_env
158
- ENV['MOBILIZE_ENV']='test'
159
- ::Resque.redis="localhost:9736"
160
- mongoid_config_path = "#{Mobilize::Base.root}/config/mongoid.yml"
161
- Mongoid.load!(mongoid_config_path, Mobilize::Base.env)
162
- end
163
-
164
157
  def Jobtracker.run_notifications
165
158
  if Jobtracker.notif_due?
166
159
  notifs = []
@@ -180,7 +173,7 @@ module Mobilize
180
173
  notifs << n
181
174
  end
182
175
  notifs.each do |notif|
183
- Emailer.write(n['subj'],notif['body']).deliver
176
+ Email.write(n['subj'],notif['body']).deliver
184
177
  Jobtracker.last_notification=Time.now.utc.to_s
185
178
  "Sent notification at #{Jobtracker.last_notification}".oputs
186
179
  end
@@ -204,5 +197,41 @@ module Mobilize
204
197
  Jobtracker.update_status("told to stop")
205
198
  return true
206
199
  end
200
+
201
+
202
+ #test methods
203
+ def Jobtracker.restart_test_redis
204
+ Jobtracker.stop_test_redis
205
+ if !system("which redis-server")
206
+ raise "** can't find `redis-server` in your path, you need redis to run Resque and Mobilize"
207
+ end
208
+ "redis-server #{Base.root}/test/redis-test.conf".bash
209
+ end
210
+
211
+ def Jobtracker.stop_test_redis
212
+ processes = `ps -A -o pid,command | grep [r]edis-test`.split($/)
213
+ pids = processes.map { |process| process.split(" ")[0] }
214
+ puts "Killing test redis server..."
215
+ pids.each { |pid| Process.kill("TERM", pid.to_i) }
216
+ puts "removing redis db dump file"
217
+ sleep 5
218
+ `rm -f #{Base.root}/test/dump.rdb #{Base.root}/test/dump-cluster.rdb`
219
+ end
220
+
221
+ def Jobtracker.set_test_env
222
+ ENV['MOBILIZE_ENV']='test'
223
+ ::Resque.redis="localhost:9736"
224
+ mongoid_config_path = "#{Base.root}/config/mongoid.yml"
225
+ Mongoid.load!(mongoid_config_path, Base.env)
226
+ end
227
+
228
+ def Jobtracker.drop_test_db
229
+ Jobtracker.set_test_env
230
+ Mongoid.session(:default).collections.each do |collection|
231
+ unless collection.name =~ /^system\./
232
+ collection.drop
233
+ end
234
+ end
235
+ end
207
236
  end
208
237
  end
@@ -22,7 +22,7 @@ module Mobilize
22
22
  if dst.last_cached_at and (dst.cache_expire_at.nil? or dst.cache_expire_at > Time.now.utc)
23
23
  return dst.read_cache
24
24
  else
25
- return dst.handler.humanize.constantize.read_by_dst_id(dst.id.to_s)
25
+ return "Mobilize::#{dst.handler.humanize}".constantize.read_by_dst_id(dst.id.to_s)
26
26
  end
27
27
  end
28
28
 
@@ -52,18 +52,18 @@ module Mobilize
52
52
  def read_cache
53
53
  dst = self
54
54
  dst.update_attributes(:last_read_at=>Time.now.utc)
55
- return Mongoer.read_by_filename(dst.id.to_s)
55
+ return Mongodb.read_by_filename(dst.id.to_s)
56
56
  end
57
57
 
58
58
  def write_cache(string,expire_at=nil)
59
59
  dst = self
60
- Mongoer.write_by_filename(dst.id.to_s,string)
60
+ Mongodb.write_by_filename(dst.id.to_s,string)
61
61
  dst.update_attributes(:last_cached_at=>Time.now.utc,:cache_expire_at=>expire_at,:size=>string.length)
62
62
  return true
63
63
  end
64
64
 
65
65
  def delete_cache
66
- return Mongoer.delete_by_filename(dst.id.to_s)
66
+ return Mongodb.delete_by_filename(dst.id.to_s)
67
67
  end
68
68
 
69
69
  end