mobilize-base 1.0.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,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