mobilize-base 1.0.1 → 1.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.
@@ -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