mobilize-base 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,296 @@
1
+ module GoogleDrive
2
+ class ClientLoginFetcher
3
+ def request_raw(method, url, data, extra_header, auth)
4
+ #this is patched to handle server errors due to http chaos
5
+ uri = URI.parse(url)
6
+ response = nil
7
+ attempts = 0
8
+ sleep_time = nil
9
+ #try 5 times to make the call
10
+ while (response.nil? or response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}) and attempts < 5
11
+ #instantiate http object, set params
12
+ http = @proxy.new(uri.host, uri.port)
13
+ http.use_ssl = true
14
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
15
+ #set 600 to allow for large downloads
16
+ http.read_timeout = 600
17
+ response = self.http_call(http, method, uri, data, extra_header, auth)
18
+ if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
19
+ if response.body.downcase.index("rate limit") or response.body.downcase.index("captcha")
20
+ if sleep_time
21
+ sleep_time = sleep_time * attempts
22
+ else
23
+ sleep_time = (rand*100).to_i
24
+ end
25
+ else
26
+ sleep_time = 10
27
+ end
28
+ attempts += 1
29
+ puts "Sleeping for #{sleep_time.to_s} due to #{response.body}"
30
+ sleep sleep_time
31
+ end
32
+ end
33
+ raise response.body if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
34
+ return response
35
+ end
36
+ def http_call(http, method, uri, data, extra_header, auth)
37
+ http.read_timeout = 600
38
+ http.start() do
39
+ path = uri.path + (uri.query ? "?#{uri.query}" : "")
40
+ header = auth_header(auth).merge(extra_header)
41
+ if method == :delete || method == :get
42
+ http.__send__(method, path, header)
43
+ else
44
+ http.__send__(method, path, data, header)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ class Acl
50
+ def update_role(entry, role) #:nodoc:
51
+ #do not send email notifications
52
+ url_suffix = "?send-notification-emails=false"
53
+ header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
54
+ doc = @session.request(
55
+ :put, %{#{entry.edit_url}#{url_suffix}}, :data => entry.to_xml(), :header => header, :auth => :writely)
56
+
57
+ entry.params = entry_to_params(doc.root)
58
+ return entry
59
+ end
60
+
61
+ def push(entry)
62
+ #do not send email notifications
63
+ entry = AclEntry.new(entry) if entry.is_a?(Hash)
64
+ url_suffix = "?send-notification-emails=false"
65
+ header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
66
+ doc = @session.request(:post, "#{@acls_feed_url}#{url_suffix}", :data => entry.to_xml(), :header => header, :auth => :writely)
67
+ entry.params = entry_to_params(doc.root)
68
+ @acls.push(entry)
69
+ return entry
70
+ end
71
+ end
72
+
73
+ class File
74
+
75
+ def add_worker_acl
76
+ f = self
77
+ return true if f.has_worker_acl?
78
+ Mobilize::Gdriver.worker_emails.each do |a|
79
+ f.update_acl(a)
80
+ end
81
+ end
82
+
83
+ def add_admin_acl
84
+ f = self
85
+ #admin includes workers
86
+ return true if f.has_admin_acl?
87
+ (Mobilize::Gdriver.admin_emails + Mobilize::Gdriver.worker_emails).each do |a|
88
+ f.update_acl(a)
89
+ end
90
+ end
91
+
92
+ def has_admin_acl?
93
+ f = self
94
+ curr_emails = f.acls.map{|a| a.scope}.sort
95
+ admin_emails = Mobilize::Gdriver.admin_emails.sort
96
+ if (curr_emails & admin_emails) == admin_emails
97
+ return true
98
+ else
99
+ return false
100
+ end
101
+ end
102
+
103
+ def has_worker_acl?
104
+ f = self
105
+ curr_emails = f.acls.map{|a| a.scope}.sort
106
+ worker_emails = Mobilize::Gdriver.worker_emails.sort
107
+ if (curr_emails & worker_emails) == worker_emails
108
+ return true
109
+ else
110
+ return false
111
+ end
112
+ end
113
+
114
+ def update_acl(email,role="writer")
115
+ f = self
116
+ #need these flags for HTTP retries
117
+ #create req_acl hash to add to current acl
118
+ if entry = f.acl_entry(email)
119
+ if [nil,"none","delete"].include?(role)
120
+ f.acl.delete(entry)
121
+ elsif entry.role != role and ['reader','writer','owner'].include?(role)
122
+ entry.role=role
123
+ f.acl.update_role(entry,entry.role)
124
+ elsif !['reader','writer','owner'].include?(role)
125
+ raise "Invalid role #{role}"
126
+ end
127
+ else
128
+ f.acl.push({:scope_type=>"user",:scope=>email,:role=>role})
129
+ end
130
+ return true
131
+ end
132
+ def acls
133
+ f = self
134
+ f.acl.to_enum.to_a
135
+ end
136
+ def acl_entry(email)
137
+ f = self
138
+ f.acls.select{|a| ['group','user'].include?(a.scope_type) and a.scope == email}.first
139
+ end
140
+
141
+ def entry_hash
142
+ f = self
143
+ dfe_xml = f.document_feed_entry.to_xml
144
+ begin
145
+ Hash.from_xml(dfe_xml)[:entry]
146
+ rescue
147
+ {}
148
+ end
149
+ end
150
+ end
151
+
152
+ class Worksheet
153
+ def to_tsv
154
+ sheet = self
155
+ rows = sheet.rows
156
+ header = rows.first
157
+ return nil unless header and header.first.to_s.length>0
158
+ #look for blank cols to indicate end of row
159
+ row_last_i = (header.index("") || header.length)-1
160
+ rows.map{|r| r[0..row_last_i]}.map{|r| r.join("\t")}.join("\n")
161
+ end
162
+ def write(tsv,check=true,job_id=nil)
163
+ sheet = self
164
+ tsvrows = tsv.split("\n")
165
+ #no rows, no write
166
+ return true if tsvrows.length==0
167
+ headers = tsvrows.first.split("\t")
168
+ batch_start = 0
169
+ batch_length = 80
170
+ rows_written = 0
171
+ curr_rows = sheet.num_rows
172
+ curr_cols = sheet.num_cols
173
+ pct_tens_complete =["0"]
174
+ curr_pct_complete = "00"
175
+ #make sure sheet is at least as big as necessary
176
+ if tsvrows.length != curr_rows
177
+ sheet.max_rows = tsvrows.length
178
+ sheet.save
179
+ end
180
+ if headers.length != curr_cols
181
+ sheet.max_cols = headers.length
182
+ sheet.save
183
+ end
184
+ #write to sheet in batches of batch_length
185
+ while batch_start < tsvrows.length
186
+ batch_end = batch_start + batch_length
187
+ tsvrows[batch_start..batch_end].each_with_index do |row,row_i|
188
+ rowcols = row.split("\t")
189
+ rowcols.each_with_index do |col_v,col_i|
190
+ sheet[row_i+batch_start+1,col_i+1]= %{#{col_v}}
191
+ end
192
+ end
193
+ sheet.save
194
+ batch_start += (batch_length + 1)
195
+ rows_written+=batch_length
196
+ if batch_start>tsvrows.length+1
197
+ if job_id
198
+ newstatus = "100 pct written at #{Time.now.utc}"
199
+ Mobilize::Job.find(job_id).update_status(newstatus)
200
+ newstatus.oputs
201
+ end
202
+ break
203
+ else
204
+ #pad digit
205
+ curr_pct_complete = "%02d" % ((rows_written+1).to_f*100/tsvrows.length.to_f).round(0)
206
+ if !pct_tens_complete.include?(curr_pct_complete.first)
207
+ if job_id
208
+ newstatus = "#{curr_pct_complete} pct written at #{Time.now.utc}"
209
+ Mobilize::Job.find(job_id).update_status(newstatus)
210
+ newstatus.oputs
211
+ pct_tens_complete << curr_pct_complete.first
212
+ end
213
+ end
214
+ end
215
+ end
216
+ #checksum it against the source
217
+ sheet.checksum(tsv) if check
218
+ true
219
+ end
220
+ def checksum(tsv)
221
+ sheet = self
222
+ sheet.reload
223
+ #loading remote data for checksum
224
+ rem_tsv = sheet.to_tsv
225
+ rem_table = rem_tsv.split("\n").map{|r| r.split("\t").map{|v| v.googlesafe}}
226
+ loc_table = tsv.split("\n").map{|r| r.split("\t").map{|v| v.googlesafe}}
227
+ re_col_vs = []
228
+ errcnt = 0
229
+ #checking cells
230
+ loc_table.each_with_index do |loc_row,row_i|
231
+ loc_row.each_with_index do |loc_v,col_i|
232
+ rem_row = rem_table[row_i]
233
+ if rem_row.nil?
234
+ errcnt+=1
235
+ "No Row #{row_i} for Write Dst".oputs
236
+ break
237
+ else
238
+ rem_v = rem_table[row_i][col_i]
239
+ if loc_v != rem_v
240
+ if ['true','false'].include?(loc_v.downcase)
241
+ #google sheet upcases true and false. ignore
242
+ elsif loc_v.starts_with?('rp') and rem_v.starts_with?('Rp')
243
+ # some other math bs
244
+ sheet[row_i+1,col_i+1] = %{'#{loc_v}}
245
+ re_col_vs << {'row_i'=>row_i+1,'col_i'=>col_i+1,'col_v'=>%{'#{loc_v}}}
246
+ elsif (loc_v.to_s.count('e')==1 or loc_v.to_s.count('e')==0) and
247
+ loc_v.to_s.sub('e','').to_i.to_s==loc_v.to_s.sub('e','').gsub(/\A0+/,"") #trim leading zeroes
248
+ #this is a string in scentific notation, or a numerical string with a leading zero
249
+ #GDocs handles this poorly, need to rewrite write_dst cells by hand with a leading apostrophe for text
250
+ sheet[row_i+1,col_i+1] = %{'#{loc_v}}
251
+ re_col_vs << {'row_i'=>row_i+1,'col_i'=>col_i+1,'col_v'=>%{'#{loc_v}}}
252
+ elsif loc_v.class==Float or loc_v.class==Fixnum
253
+ if (loc_v - rem_v.to_f).abs>0.0001
254
+ "row #{row_i.to_s} col #{col_i.to_s}: Local=>#{loc_v.to_s} , Remote=>#{rem_v.to_s}".oputs
255
+ errcnt+=1
256
+ end
257
+ elsif rem_v.class==Float or rem_v.class==Fixnum
258
+ if (rem_v - loc_v.to_f).abs>0.0001
259
+ "row #{row_i.to_s} col #{col_i.to_s}: Local=>#{loc_v.to_s} , Remote=>#{rem_v.to_s}".oputs
260
+ errcnt+=1
261
+ end
262
+ elsif loc_v.to_s.is_time?
263
+ rem_time = begin
264
+ Time.parse(rem_v.to_s)
265
+ rescue
266
+ nil
267
+ end
268
+ if rem_time.nil? || ((loc_v - rem_time).abs>1)
269
+ "row #{row_i.to_s} col #{col_i.to_s}: Local=>#{loc_v} , Remote=>#{rem_v}".oputs
270
+ errcnt+=1
271
+ end
272
+ else
273
+ #"loc_v=>#{loc_v.to_s},rem_v=>#{rem_v.to_s}".oputs
274
+ if loc_v.force_encoding("UTF-8") != rem_v.force_encoding("UTF-8")
275
+ #make sure it's not an ecoding issue
276
+ "row #{row_i.to_s} col #{col_i.to_s}: Local=>#{loc_v} , Remote=>#{rem_v}".oputs
277
+ errcnt+=1
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+ if errcnt==0
285
+ if re_col_vs.length>0
286
+ sheet.save
287
+ "rewrote:#{re_col_vs.to_s}".oputs
288
+ else
289
+ true
290
+ end
291
+ else
292
+ raise "#{errcnt} errors found in checksum"
293
+ end
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,86 @@
1
+
2
+ class Hash
3
+ #defined read and write methods for hashes to get around annoying mkpath issues
4
+ def read_path(array)
5
+ result=self
6
+ array.each_with_index do |p,i|
7
+ if result.class==Hash and result[p]
8
+ result=result[p]
9
+ elsif i==array.length-1
10
+ return result[p]
11
+ else
12
+ return nil
13
+ end
14
+ end
15
+ return result
16
+ end
17
+ #write array of arbitrary depth to the hash
18
+ def write_path(array,data)
19
+ return self[array.first.to_s]=data if array.length==1
20
+ array.each_with_index do |m,i|
21
+ arr_s = array[0..i]
22
+ if !self.read_path(arr_s)
23
+ eval(%{self["#{arr_s.join('"]["')}"]={}})
24
+ elsif self.read_path(arr_s).class != Hash
25
+ raise "There is data at #{arr_s.join("=>")}"
26
+ end
27
+ end
28
+ eval(%{self["#{array[0..-2].join('"]["')}"]}).store(array[-1].to_s,data)
29
+ return self
30
+ end
31
+ # BEGIN methods to create hash from XML
32
+ class << self
33
+ def from_xml(xml_io)
34
+ begin
35
+ result = Nokogiri::XML(xml_io)
36
+ return { result.root.name.to_sym => xml_node_to_hash(result.root)}
37
+ rescue Exception => e
38
+ # raise your custom exception here
39
+ end
40
+ end
41
+ def xml_node_to_hash(node)
42
+ # If we are at the root of the document, start the hash
43
+ if node.element?
44
+ result_hash = {}
45
+ if node.attributes != {}
46
+ result_hash[:attributes] = {}
47
+ node.attributes.keys.each do |key|
48
+ result_hash[:attributes][node.attributes[key].name.to_sym] = prepare(node.attributes[key].value)
49
+ end
50
+ end
51
+ if node.children.size > 0
52
+ node.children.each do |child|
53
+ result = xml_node_to_hash(child)
54
+
55
+ if child.name == "text"
56
+ unless child.next_sibling || child.previous_sibling
57
+ return prepare(result)
58
+ end
59
+ elsif result_hash[child.name.to_sym]
60
+ if result_hash[child.name.to_sym].is_a?(Object::Array)
61
+ result_hash[child.name.to_sym] << prepare(result)
62
+ else
63
+ result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << prepare(result)
64
+ end
65
+ else
66
+ result_hash[child.name.to_sym] = prepare(result)
67
+ end
68
+ end
69
+
70
+ return result_hash
71
+ else
72
+ return result_hash
73
+ end
74
+ else
75
+ return prepare(node.content.to_s)
76
+ end
77
+ end
78
+ def prepare(data)
79
+ (data.class == String && data.to_i.to_s == data) ? data.to_i : data
80
+ end
81
+ end
82
+ def to_struct(struct_name)
83
+ Struct.new(struct_name,*keys).new(*values)
84
+ end
85
+ # END methods to create hash from XML
86
+ end
@@ -0,0 +1,6 @@
1
+ class Object
2
+ #alias for instance_eval
3
+ def ie(&blk)
4
+ self.instance_eval(&blk)
5
+ end
6
+ end
@@ -0,0 +1,180 @@
1
+ module Mobilize
2
+ module Resque
3
+ def Resque.config
4
+ Base.config('resque')[Base.env]
5
+ end
6
+
7
+ def Resque.queue_name
8
+ Resque.config['queue_name']
9
+ end
10
+
11
+ def Resque.queues
12
+ Base.queues
13
+ end
14
+
15
+ def Resque.log_path
16
+ Base.log_path("mobilize-resque-#{::Mobilize::Base.env}")
17
+ end
18
+
19
+ def Resque.workers(state="all")
20
+ raise "invalid state #{state}" unless ['all','idle','working','timeout'].include?(state)
21
+ workers = ::Resque.workers.select{|w| w.queues.first == Resque.queue_name}
22
+ return workers if state == 'all'
23
+ working_workers = workers.select{|w| w.job['queue']== Resque.queue_name}
24
+ return working_workers if state == 'working'
25
+ idle_workers = workers.select{|w| w.job['queue'].nil?}
26
+ return idle_workers if state == 'idle'
27
+ timeout_workers = workers.select{|w| w.job['payload'] and w.job['payload']['class']!='Jobtracker' and w.job['runat'] < (Time.now.utc - Jobtracker.max_run_time)}
28
+ return timeout_workers if state == 'timeout'
29
+ end
30
+
31
+ def Resque.failures
32
+ ::Resque::Failure.all(0,0).select{|f| f['queue'] == Resque.queue_name}
33
+ end
34
+
35
+ #active state refers to jobs that are either queued or working
36
+ def Resque.jobs(state="active")
37
+ raise "invalid state #{state}" unless ['all','queued','working','active','timeout','failed'].include?(state)
38
+ working_jobs = Resque.workers('working').map{|w| w.job['payload']}
39
+ return working_jobs if state == 'working'
40
+ queued_jobs = ::Resque.peek(Resque.queue_name,0,0).to_a
41
+ return queued_jobs if state == 'queued'
42
+ return working_jobs + queued_jobs if state == 'active'
43
+ failed_jobs = Resque.failures.map{|f| f['payload']}
44
+ return failed_jobs if state == 'failed'
45
+ timeout_jobs = Resque.workers("timeout").map{|w| w.job['payload']}
46
+ return timeout_jobs if state == 'timeout'
47
+ return working_jobs + queued_jobs + failed_jobs if state == 'all'
48
+ end
49
+
50
+ def Resque.active_mongo_ids
51
+ #first argument of the payload is the mongo id in Mongo unless the worker is Jobtracker
52
+ Resque.jobs('active').map{|j| j['args'].first unless j['class']=='Jobtracker'}.compact
53
+ end
54
+
55
+ #Resque workers and methods to find
56
+ def Resque.find_worker_by_mongo_id(mongo_id)
57
+ Resque.workers('working').select{|w| w.job['payload']['args'][0] == mongo_id}.first
58
+ end
59
+
60
+ def Resque.update_job_status(mongo_id,msg)
61
+ #this only works on working workers
62
+ worker = Resque.find_worker_by_mongo_id(mongo_id)
63
+ #also fire a log, cap logfiles at 10 MB
64
+ if !worker
65
+ Logger.new(Resque.log_path, 10, 1024*1000*10).info("[no worker for #{mongo_id}: #{Time.now.utc}] status: #{msg}")
66
+ return false
67
+ end
68
+ Resque.set_worker_args(worker,{"status"=>msg})
69
+ Logger.new(Resque.log_path, 10, 1024*1000*10).info("[#{worker} #{Time.now.utc}] status: #{msg}")
70
+ return true
71
+ end
72
+
73
+ def Resque.update_job_email(mongo_id,email)
74
+ #this only works on working workers
75
+ worker = Resque.find_worker_by_mongo_id(mongo_id)
76
+ return false unless worker
77
+ Resque.set_worker_args(worker,{"email"=>email})
78
+ #also fire a log, cap logfiles at 10 MB
79
+ Logger.new(Resque.log_path, 10, 1024*1000*10).info("[#{worker} #{Time.now.utc}] email: #{email}")
80
+ end
81
+
82
+ def Resque.get_worker_args(worker)
83
+ key = "worker:#{worker}"
84
+ json = ::Resque.redis.get(key)
85
+ if json
86
+ hash = JSON.parse(json)
87
+ hash['payload']['args'].last
88
+ end
89
+ end
90
+
91
+ #takes a worker and invokes redis to set the last value in its second arg array element
92
+ #by our convention this is a Hash
93
+ def Resque.set_worker_args(worker,args)
94
+ key = "worker:#{worker}"
95
+ json = ::Resque.redis.get(key)
96
+ if json
97
+ hash = JSON.parse(json)
98
+ payload_args = hash['payload']['args']
99
+ #jobmaster only gets one arg
100
+ if payload_args[1].nil?
101
+ payload_args[1] = args
102
+ else
103
+ payload_args[1] = {} unless payload_args[1].class==Hash
104
+ args.keys.each{|k,v| payload_args[1][k] = args[k]}
105
+ end
106
+ ::Resque.redis.set(key,hash.to_json)
107
+ return true
108
+ else
109
+ return false
110
+ end
111
+ end
112
+
113
+ def Resque.failure_report
114
+ fjobs = {}
115
+ excs = Hash.new(0)
116
+ Resque.failures.each do |f|
117
+ sname = f['payload']['class'] + ("=>" + f['payload']['args'].second['name'].to_s if f['payload']['args'].second).to_s
118
+ excs = f['error']
119
+ if fjobs[sname].nil?
120
+ fjobs[sname] = {excs => 1}
121
+ elsif fjobs[sname][excs].nil?
122
+ fjobs[sname][excs] = 1
123
+ else
124
+ fjobs[sname][excs] += 1
125
+ end
126
+ end
127
+ return fjobs
128
+ end
129
+
130
+ def Resque.start_workers(count=1)
131
+ count.times do
132
+ "(cd #{Base.root};rake MOBILIZE_ENV=#{Base.env} mobilize:work) >> #{Resque.log_path} 2>&1 &".bash
133
+ end
134
+ end
135
+
136
+ def Resque.kill_idle_workers(count=nil)
137
+ idle_pids = Resque.workers('idle').select{|w| w.job=={}}.map{|w| w.to_s.split(":").second}
138
+ if count>idle_pids.length or count == 0
139
+ return false
140
+ elsif count
141
+ "kill #{idle_pids[0..count-1].join(" ")}".bash
142
+ else
143
+ "kill #{idle_pids.join(" ")}".bash
144
+ end
145
+ return true
146
+ end
147
+
148
+ def Resque.kill_workers(count=nil)
149
+ pids = Resque.workers.map{|w| w.to_s.split(":").second}
150
+ if count.to_i > pids.length or count == 0
151
+ return false
152
+ elsif count
153
+ "kill #{pids[0..count-1].join(" ")}".bash(false)
154
+ elsif pids.length>0
155
+ "kill #{pids.join(" ")}".bash(false)
156
+ else
157
+ return false
158
+ end
159
+ return true
160
+ end
161
+
162
+ def Resque.prep_workers(max_workers=Resque.config['max_workers'])
163
+ curr_workers = Resque.workers.length
164
+ if curr_workers > max_workers
165
+ #kill as many idlers as necessary
166
+ Resque.kill_idle_workers(curr_workers - max_workers)
167
+ #wait a few secs for these guys to die
168
+ sleep 10
169
+ curr_workers = Resque.workers.length
170
+ if curr_workers > max_workers
171
+ #kill working workers
172
+ Resque.kill_workers(curr_workers - max_workers)
173
+ end
174
+ else
175
+ Resque.start_workers(max_workers-curr_workers)
176
+ end
177
+ return true
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,94 @@
1
+ class String
2
+ def to_a
3
+ return [self]
4
+ end
5
+ def oputs
6
+ STDOUT.puts self
7
+ end
8
+ def eputs
9
+ STDERR.puts self
10
+ end
11
+ def opp
12
+ pp self
13
+ end
14
+ def bash(except=true)
15
+ pid,stdin,stdout,stderr = Open4.popen4(self)
16
+ raise stderr.read if (stderr.read.length>0 and except==true)
17
+ return stdout.read
18
+ end
19
+ def googlesafe
20
+ v=self
21
+ return "" if v.to_s==""
22
+ #normalize numbers by removing '$', '%', ',', ' '
23
+ vnorm = v.to_s.norm_num
24
+ vdigits = vnorm.split(".").last.length
25
+ if vnorm.to_f.to_s=="Infinity"
26
+ #do nothing
27
+ elsif ("%.#{vdigits}f" % vnorm.to_f.to_s)==vnorm
28
+ #round floats to 5 sig figs
29
+ v=vnorm.to_f.round(5)
30
+ elsif vnorm.to_i.to_s==vnorm
31
+ #make sure integers are cast as such
32
+ v=vnorm.to_i
33
+ elsif v.is_time?
34
+ begin
35
+ time_vs = v.split("/")
36
+ if time_vs.first.length<=2 and time_vs.second.length<=2
37
+ #date is of the form mm/dd/yyyy or mm/dd/yy
38
+ v=Time.parse("#{time_vs[2][0..3]}/#{time_vs[0]}/#{time_vs[1]}#{time_vs[2][4..-1]}")
39
+ else
40
+ v=Time.parse(v)
41
+ end
42
+ rescue
43
+ #do nothing
44
+ end
45
+ end
46
+ return v
47
+ end
48
+ def norm_num
49
+ return self.gsub(",","").gsub("$","").gsub("%","").gsub(" ","")
50
+ end
51
+ def is_float?
52
+ return self.norm_num.to_f.to_s == self.norm_num.to_s
53
+ end
54
+ def is_fixnum?
55
+ return self.norm_num.to_i.to_s == self.norm_num.to_s
56
+ end
57
+ def is_time?
58
+ if ((self.count("-")==2 or self.count("/")==2) and self.length>=8 and self.length<=20)
59
+ return true
60
+ end
61
+ split_str = self.split(" ")
62
+ if split_str.length==3 and
63
+ split_str.first.count("-")==2 and
64
+ split_str.last.first=="-" and
65
+ split_str.second.count(":")==2
66
+ return true
67
+ end
68
+ end
69
+ def json_to_hash
70
+ begin
71
+ return JSON.parse(self)
72
+ rescue => exc
73
+ return {}
74
+ end
75
+ end
76
+ def tsv_to_hash_array
77
+ rows = self.split("\n")
78
+ return [] if rows.first.nil?
79
+ return [{rows.first=>nil}] if (rows.length==2 and rows.second==nil)
80
+ headers = rows.first.split("\t")
81
+ if rows.length==1
82
+ #return single member hash with all nil values
83
+ return [headers.map{|k| {k=>nil}}.inject{|k,h| h.merge(k)}]
84
+ end
85
+ row_hash_arr =[]
86
+ rows[1..-1].each do |row|
87
+ cols = row.split("\t")
88
+ row_hash = {}
89
+ headers.each_with_index{|h,h_i| row_hash[h] = cols[h_i]}
90
+ row_hash_arr << row_hash
91
+ end
92
+ return row_hash_arr
93
+ end
94
+ end
@@ -0,0 +1,24 @@
1
+ module Mobilize
2
+ require 'action_mailer'
3
+ class Emailer < ActionMailer::Base
4
+ ActionMailer::Base.delivery_method = :smtp
5
+
6
+ ActionMailer::Base.smtp_settings = {
7
+ :address => "smtp.gmail.com",
8
+ :port => 587,
9
+ :domain => 'ngmoco.com',
10
+ :user_name => Gdriver.owner_email,
11
+ :password => Gdriver.password(Gdriver.owner_email),
12
+ :authentication => 'plain',
13
+ :enable_starttls_auto => true }
14
+
15
+ def write(subj,
16
+ bod="",
17
+ recipient=Jobtracker.admin_emails.join(","))
18
+ mail(:from=>Gdriver.owner_email,
19
+ :to=>recipient,
20
+ :subject=>subj,
21
+ :body=>bod)
22
+ end
23
+ end
24
+ end