mobilize-base 1.0.2 → 1.0.4

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.
Files changed (38) hide show
  1. data/.gitignore +5 -0
  2. data/LICENSE.txt +202 -20
  3. data/README.md +219 -138
  4. data/Rakefile +1 -2
  5. data/lib/mobilize-base/extensions/google_drive/acl.rb +25 -0
  6. data/lib/mobilize-base/extensions/google_drive/client_login_fetcher.rb +49 -0
  7. data/lib/mobilize-base/extensions/google_drive/file.rb +80 -0
  8. data/lib/mobilize-base/extensions/{google_drive.rb → google_drive/worksheet.rb} +46 -173
  9. data/lib/mobilize-base/extensions/resque.rb +18 -24
  10. data/lib/mobilize-base/extensions/string.rb +12 -0
  11. data/lib/mobilize-base/handlers/gbook.rb +14 -47
  12. data/lib/mobilize-base/handlers/gdrive.rb +17 -18
  13. data/lib/mobilize-base/handlers/gfile.rb +18 -39
  14. data/lib/mobilize-base/handlers/gridfs.rb +43 -0
  15. data/lib/mobilize-base/handlers/gsheet.rb +48 -99
  16. data/lib/mobilize-base/jobtracker.rb +29 -15
  17. data/lib/mobilize-base/models/dataset.rb +33 -35
  18. data/lib/mobilize-base/models/job.rb +21 -168
  19. data/lib/mobilize-base/models/runner.rb +178 -0
  20. data/lib/mobilize-base/models/task.rb +137 -0
  21. data/lib/mobilize-base/models/user.rb +47 -0
  22. data/lib/mobilize-base/rakes.rb +59 -0
  23. data/lib/mobilize-base/version.rb +1 -1
  24. data/lib/mobilize-base.rb +20 -9
  25. data/lib/samples/gdrive.yml +12 -12
  26. data/lib/samples/gridfs.yml +9 -0
  27. data/lib/samples/gsheet.yml +6 -0
  28. data/lib/samples/jobtracker.yml +9 -9
  29. data/lib/samples/mongoid.yml +3 -3
  30. data/mobilize-base.gemspec +1 -1
  31. data/test/base1_task1.yml +3 -0
  32. data/test/base_job_rows.yml +13 -0
  33. data/test/mobilize-base_test.rb +59 -0
  34. metadata +20 -9
  35. data/lib/mobilize-base/handlers/mongodb.rb +0 -32
  36. data/lib/mobilize-base/models/requestor.rb +0 -232
  37. data/lib/mobilize-base/tasks.rb +0 -43
  38. data/test/mobilize_test.rb +0 -108
@@ -1,7 +1,7 @@
1
1
  module Mobilize
2
2
  module Jobtracker
3
3
  def Jobtracker.config
4
- Base.config('jobtracker')[Base.env]
4
+ Base.config('jobtracker')
5
5
  end
6
6
 
7
7
  #modify this to increase the frequency of request cycles
@@ -14,8 +14,8 @@ module Mobilize
14
14
  Jobtracker.config['notification_freq']
15
15
  end
16
16
 
17
- def Jobtracker.requestor_refresh_freq
18
- Jobtracker.config['requestor_refresh_freq']
17
+ def Jobtracker.runner_read_freq
18
+ Jobtracker.config['runner_read_freq']
19
19
  end
20
20
 
21
21
  #long running tolerance
@@ -28,11 +28,11 @@ module Mobilize
28
28
  end
29
29
 
30
30
  def Jobtracker.admin_emails
31
- Jobtracker.admins.map{|a| a['email']}
31
+ Jobtracker.admins.map{|a| a['email'] }
32
32
  end
33
33
 
34
34
  def Jobtracker.worker
35
- Resque.find_worker_by_mongo_id("jobtracker")
35
+ Resque.find_worker_by_path("jobtracker")
36
36
  end
37
37
 
38
38
  def Jobtracker.workers(state="all")
@@ -49,7 +49,7 @@ module Mobilize
49
49
 
50
50
  def Jobtracker.update_status(msg)
51
51
  #Jobtracker has no persistent database state
52
- Resque.update_job_status("jobtracker",msg)
52
+ Resque.set_worker_args_by_path("jobtracker",{'status'=>msg})
53
53
  return true
54
54
  end
55
55
 
@@ -96,7 +96,7 @@ module Mobilize
96
96
  end
97
97
 
98
98
  def Jobtracker.enqueue!
99
- ::Resque::Job.create(Resque.queue_name, Jobtracker, 'jobtracker',{'status'=>'working'})
99
+ ::Resque::Job.create(Resque.queue_name, Jobtracker, 'jobtracker',{})
100
100
  end
101
101
 
102
102
  def Jobtracker.restart!
@@ -107,8 +107,9 @@ module Mobilize
107
107
 
108
108
  def Jobtracker.restart_workers!
109
109
  Jobtracker.kill_workers
110
- sleep 5
110
+ sleep 10
111
111
  Jobtracker.prep_workers
112
+ Jobtracker.update_status("put workers back on the queue")
112
113
  end
113
114
 
114
115
  def Jobtracker.stop!
@@ -117,7 +118,7 @@ module Mobilize
117
118
  sleep 5
118
119
  i=0
119
120
  while Jobtracker.status=='stopping'
120
- puts "#{Jobtracker.to_s} still on queue, waiting"
121
+ Jobtracker.update_status("#{Jobtracker.to_s} still on queue, waiting")
121
122
  sleep 5
122
123
  i+=1
123
124
  end
@@ -175,7 +176,7 @@ module Mobilize
175
176
  notifs.each do |notif|
176
177
  Email.write(n['subj'],notif['body']).deliver
177
178
  Jobtracker.last_notification=Time.now.utc.to_s
178
- "Sent notification at #{Jobtracker.last_notification}".oputs
179
+ Jobtracker.update_status("Sent notification at #{Jobtracker.last_notification}")
179
180
  end
180
181
  end
181
182
  return true
@@ -183,13 +184,14 @@ module Mobilize
183
184
 
184
185
  def Jobtracker.perform(id,*args)
185
186
  while Jobtracker.status != 'stopping'
186
- requestors = Requestor.all
187
+ users = User.all
187
188
  Jobtracker.run_notifications
188
- requestors.each do |r|
189
- Jobtracker.update_status("Checking requestor #{r.name}")
189
+ users.each do |u|
190
+ r = u.runner
191
+ Jobtracker.update_status("Checking #{r.path}")
190
192
  if r.is_due?
191
193
  r.enqueue!
192
- Jobtracker.update_status("Enqueued requestor #{r.name}")
194
+ Jobtracker.update_status("Enqueued #{r.path}")
193
195
  end
194
196
  end
195
197
  sleep 5
@@ -221,7 +223,7 @@ module Mobilize
221
223
  def Jobtracker.set_test_env
222
224
  ENV['MOBILIZE_ENV']='test'
223
225
  ::Resque.redis="localhost:9736"
224
- mongoid_config_path = "#{Base.root}/config/mongoid.yml"
226
+ mongoid_config_path = "#{Base.root}/config/mobilize/mongoid.yml"
225
227
  Mongoid.load!(mongoid_config_path, Base.env)
226
228
  end
227
229
 
@@ -233,5 +235,17 @@ module Mobilize
233
235
  end
234
236
  end
235
237
  end
238
+
239
+ def Jobtracker.build_test_runner(user_name)
240
+ Jobtracker.set_test_env
241
+ u = User.where(:name=>user_name).first
242
+ Jobtracker.update_status("delete old books and datasets")
243
+ # delete any old runner from previous test runs
244
+ gdrive_slot = Gdrive.owner_email
245
+ u.runner.gsheet(gdrive_slot).spreadsheet.delete
246
+ Jobtracker.update_status("enqueue jobtracker, wait 45s")
247
+ Mobilize::Jobtracker.start
248
+ sleep 45
249
+ end
236
250
  end
237
251
  end
@@ -2,69 +2,67 @@ module Mobilize
2
2
  class Dataset
3
3
  include Mongoid::Document
4
4
  include Mongoid::Timestamps
5
- field :requestor_id, type: String
6
5
  field :handler, type: String
7
- field :name, type: String
6
+ field :path, type: String
8
7
  field :url, type: String
9
- field :size, type: Fixnum
8
+ field :raw_size, type: Fixnum
10
9
  field :last_cached_at, type: Time
10
+ field :last_cache_handler, type: String
11
11
  field :last_read_at, type: Time
12
12
  field :cache_expire_at, type: Time
13
13
 
14
- index({ requestor_id: 1})
15
- index({ handler: 1})
16
- index({ name: 1})
17
-
18
- before_destroy :destroy_cache
14
+ index({ handler: 1, path: 1}, { unique: true})
19
15
 
20
16
  def read
21
17
  dst = self
22
- if dst.last_cached_at and (dst.cache_expire_at.nil? or dst.cache_expire_at > Time.now.utc)
23
- return dst.read_cache
24
- else
25
- return "Mobilize::#{dst.handler.humanize}".constantize.read_by_dst_id(dst.id.to_s)
26
- end
18
+ return "Mobilize::#{dst.handler.humanize}".constantize.read_by_path(dst.path)
27
19
  end
28
20
 
29
- def Dataset.find_by_handler_and_name(handler,name)
30
- Dataset.where(handler: handler, name: name).first
31
- end
32
-
33
- def Dataset.find_or_create_by_handler_and_name(handler,name)
34
- dst = Dataset.where(handler: handler, name: name).first
35
- dst = Dataset.create(handler: handler, name: name) unless dst
36
- return dst
21
+ def Dataset.find_by_handler_and_path(handler,path)
22
+ Dataset.where(handler: handler, path: path).first
37
23
  end
38
24
 
39
- def Dataset.find_or_create_by_requestor_id_and_handler_and_name(requestor_id,handler,name)
40
- dst = Dataset.where(requestor_id: requestor_id, handler: handler, name: name).first
41
- dst = Dataset.create(requestor_id: requestor_id, handler: handler, name: name) unless dst
25
+ def Dataset.find_or_create_by_handler_and_path(handler,path)
26
+ dst = Dataset.where(handler: handler, path: path).first
27
+ dst = Dataset.create(handler: handler, path: path) unless dst
42
28
  return dst
43
29
  end
44
30
 
45
- def write(data)
31
+ def write(string)
46
32
  dst = self
47
- dst.handler.humanize.constantize.write_by_dst_id(dst.id.to_s,data)
33
+ "Mobilize::#{dst.handler.humanize}".constantize.write_by_path(dst.path,string)
34
+ dst.raw_size = string.length
48
35
  dst.save!
49
36
  return true
50
37
  end
51
38
 
52
- def read_cache
39
+ def cache_valid?
53
40
  dst = self
54
- dst.update_attributes(:last_read_at=>Time.now.utc)
55
- return Mongodb.read_by_filename(dst.id.to_s)
41
+ return true if dst.last_cached_at and (dst.cache_expire_at.nil? or dst.cache_expire_at > Time.now.utc)
56
42
  end
57
43
 
58
- def write_cache(string,expire_at=nil)
44
+ def read_cache(cache_handler="gridfs")
59
45
  dst = self
60
- Mongodb.write_by_filename(dst.id.to_s,string)
61
- dst.update_attributes(:last_cached_at=>Time.now.utc,:cache_expire_at=>expire_at,:size=>string.length)
62
- return true
46
+ if cache_valid?
47
+ dst.update_attributes(:last_read_at=>Time.now.utc)
48
+ return "Mobilize::#{cache_handler.humanize}".constantize.read([dst.handler,dst.path].join("://"))
49
+ else
50
+ raise "Cache invalid or not found for #{cache_handler}://#{dst.path}"
51
+ end
63
52
  end
64
53
 
65
- def delete_cache
66
- return Mongodb.delete_by_filename(dst.id.to_s)
54
+ def write_cache(string,expire_at=nil,cache_handler="gridfs")
55
+ dst = self
56
+ "Mobilize::#{cache_handler.humanize}".constantize.write([dst.handler,dst.path].join("://"),string)
57
+ dst.update_attributes(:last_cached_at=>Time.now.utc,
58
+ :last_cache_handler=>cache_handler.to_s.downcase,
59
+ :cache_expire_at=>expire_at,
60
+ :size=>string.length)
61
+ return true
67
62
  end
68
63
 
64
+ def delete_cache(cache_handler="gridfs")
65
+ return "Mobilize::#{cache_handler.humanize}".constantize.delete(dst.handler, dst.path)
66
+ end
69
67
  end
70
68
  end
@@ -2,199 +2,52 @@ module Mobilize
2
2
  class Job
3
3
  include Mongoid::Document
4
4
  include Mongoid::Timestamps
5
- field :requestor_id, type: String
6
- field :name, type: String
7
- field :active, type: Boolean #active, inactive
8
- field :schedule, type: String
9
- field :active_task, type: String
10
- field :tasks, type: String
5
+ field :path, type: String
6
+ field :active, type: Boolean
7
+ field :trigger, type: String
11
8
  field :status, type: String
12
- field :last_error, type: String
13
- field :last_trace, type: String
14
9
  field :last_completed_at, type: Time
15
- field :datasets, type: String #name of data sources
16
- field :params, type: String #JSON
17
- field :destination, type: String #output destination - could be file, could be sheet
18
10
 
19
- index({ requestor_id: 1})
20
- index({ name: 1})
11
+ index({ path: 1})
21
12
 
22
- before_destroy :destroy_output_dst_ids
23
-
24
- def worker
25
- j = self
26
- Mobilize::Resque.find_worker_by_mongo_id(j.id.to_s)
27
- end
28
-
29
- def dataset_array
13
+ def name
30
14
  j = self
31
- r = j.requestor
32
- dsts = j.datasets.split(",").map{|dst| dst.strip}
33
- dsts.map do |ps|
34
- #prepend jobspec title if there is no path separator
35
- full_ps = ps.index("/") ? ps : [r.jobspec_title,ps].join("/")
36
- #find or create dataset for this sheet
37
- dst = Dataset.find_or_create_by_handler_and_name("gsheet",full_ps)
38
- dst.update_attributes(:requestor_id=>r.id.to_s) unless dst.requestor_id
39
- dst
40
- end
41
- end
42
-
43
- def task_array
44
- self.tasks.split(",").map{|t| t.strip}
45
- end
46
-
47
- def task_output_dsts
48
- j = self
49
- r = j.requestor
50
- dst_names = j.task_array.map{|tname| [r.name,j.name,tname].join("/")}
51
- dst_names.map do |dst_name|
52
- Dataset.find_or_create_by_requestor_id_and_handler_and_name(r.id.to_s,'mongodb',dst_name)
53
- end
15
+ j.path.split("/").last
54
16
  end
55
17
 
56
- def task_idx
18
+ def tasks
57
19
  j = self
58
- j.task_array.index(j.active_task)
20
+ Task.where(:path=>/^#{j.path.escape_regex}/).to_a.sort_by{|t| t.path}
59
21
  end
60
22
 
61
- def Job.find_by_name(name)
62
- Job.where(:name=>name).first
63
- end
64
-
65
- def Job.find_all_by_requestor_id(requestor_id)
66
- Job.where(:requestor_id=>requestor_id).to_a
67
- end
68
-
69
- def Job.find_or_create_by_requestor_id_and_name(requestor_id,name)
70
- j = Job.where(:requestor_id=>requestor_id, :name=>name).first
71
- j = Job.create(:requestor_id=>requestor_id, :name=>name) unless j
23
+ def Job.find_or_create_by_path(path)
24
+ j = Job.where(:path=>path).first
25
+ j = Job.create(:path=>path) unless j
72
26
  return j
73
27
  end
74
28
 
75
- #called by Resque
76
- def Job.perform(id,*args)
77
- j = Job.find(id)
78
- r = j.requestor
79
- handler,method_name = j.active_task.split(".")
80
- begin
81
- j.update_status(%{Starting #{j.active_task} task at #{Time.now.utc}})
82
- task_output = "Mobilize::#{handler.humanize}".constantize.send("#{method_name}_by_job_id",id)
83
- #this allows user to return false if the stage didn't go as expected and needs to retry
84
- #e.g. tried to write to Google but all the accounts were in use
85
- return false if task_output == false
86
- task_output_dst = j.task_output_dsts[j.task_idx]
87
- task_output = task_output.to_s unless task_output.class == String
88
- task_output_dst.write_cache(task_output)
89
- if j.active_task == j.task_array.last
90
- j.active_task = nil
91
- j.last_error = ""
92
- j.last_trace = ""
93
- j.last_completed_at = Time.now.utc
94
- j.status = %{Completed all tasks at #{Time.now.utc}}
95
- j.save!
96
- #check for any dependent jobs, if there are, enqueue them
97
- r = j.requestor
98
- dep_jobs = Job.where(:active=>true, :requestor_id=>r.id.to_s, :schedule=>"after #{j.name}").to_a
99
- dep_jobs += Job.where(:active=>true, :schedule=>"after #{r.name}/#{j.name}").to_a
100
- #put begin/rescue so all dependencies run
101
- dep_jobs.each{|dj| begin;dj.enqueue! unless dj.is_working?;rescue;end}
102
- else
103
- #set next task
104
- j.active_task = j.task_array[j.task_idx+1]
105
- j.save!
106
- #queue up next task
107
- j.enqueue!
108
- end
109
- rescue ScriptError,StandardError => exc
110
- #record the failure in Job so it appears on spec sheets
111
- j.status='failed'
112
- j.save!
113
- j.update_status("Failed at #{Time.now.utc.to_s}")
114
- j.update_attributes(:last_error=>exc.to_s,:last_trace=>exc.backtrace.to_s)
115
- [exc.to_s,exc.backtrace.to_s].join("=>").oputs
116
- #raising here will cause the failure to show on the Resque UI
117
- raise exc
118
- end
119
- return true
120
- end
121
-
122
- def enqueue!
123
- j = self
124
- r = j.requestor
125
- j.update_attributes(:active_task=>j.task_array.first) if j.active_task.blank?
126
- ::Resque::Job.create("mobilize",Job,j.id.to_s,%{#{r.name}=>#{j.name}})
127
- return true
128
- end
129
-
130
29
  #convenience methods
131
- def requestor
132
- j = self
133
- return Requestor.find(j.requestor_id)
134
- end
135
-
136
- def restart
137
- j = self
138
- j.update_attributes(:last_completed_at=>nil)
139
- return true
140
- end
141
-
142
- def prior_task
143
- j = self
144
- return nil if j.active_task.nil?
145
- task_idx = j.task_array.index(j.active_task)
146
- return nil if task_idx==0
147
- return j.task_array[task_idx-1]
148
- end
149
-
150
- def destination_url
151
- j = self
152
- return nil if j.destination.nil?
153
- destination = j.destination
154
- dst = if j.task_array.last == 'gsheet.write'
155
- destination = [j.requestor.jobspec_title,j.destination].join("/") if destination.split("/").length==1
156
- Dataset.find_by_handler_and_name('gsheet',destination)
157
- elsif j.task_array.last == 'gfile.write'
158
- #all gfiles must end in gz
159
- destination += ".gz" unless destination.ends_with?(".gz")
160
- destination = [s.requestor.name,"_"].join + destination unless destination.starts_with?([s.requestor.name,"_"].join)
161
- Dataset.find_by_handler_and_name('gfile',destination)
162
- end
163
- return dst.url if dst
164
- end
165
-
166
- def worker_args
167
- j = self
168
- Jobtracker.get_worker_args(j.worker)
169
- end
170
-
171
- def set_worker_args(args)
172
- j = self
173
- Jobtracker.set_worker_args(j.worker,args)
174
- end
175
-
176
- def update_status(msg)
30
+ def runner
177
31
  j = self
178
- j.update_attributes(:status=>msg)
179
- Mobilize::Resque.update_job_status(j.id.to_s,msg)
180
- return true
32
+ runner_path = j.path.split("/")[0..-2].join("/")
33
+ return Runner.where(:path=>runner_path).first
181
34
  end
182
35
 
183
36
  def is_working?
184
37
  j = self
185
- Mobilize::Resque.active_mongo_ids.include?(j.id.to_s)
38
+ j.tasks.select{|t| t.is_working?}.compact.length>0
186
39
  end
187
40
 
188
41
  def is_due?
189
42
  j = self
190
- return false if j.is_working? or j.active == false or j.schedule.to_s.starts_with?("after")
43
+ return false if j.is_working? or j.active == false or j.trigger.to_s.starts_with?("after")
191
44
  last_run = j.last_completed_at
192
- #check schedule
193
- schedule = j.schedule
194
- return true if schedule == 'once'
45
+ #check trigger
46
+ trigger = j.trigger
47
+ return true if trigger == 'once'
195
48
  #strip the "every" from the front if present
196
- schedule = schedule.gsub("every","").gsub("."," ").strip
197
- value,unit,operator,job_utctime = schedule.split(" ")
49
+ trigger = trigger.gsub("every","").gsub("."," ").strip
50
+ value,unit,operator,job_utctime = trigger.split(" ")
198
51
  curr_utctime = Time.now.utc
199
52
  curr_utcdate = curr_utctime.to_date.strftime("%Y-%m-%d")
200
53
  if job_utctime
@@ -0,0 +1,178 @@
1
+ module Mobilize
2
+ class Runner
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ field :path, type: String
6
+ field :active, type: Boolean
7
+ field :status, type: String
8
+ field :last_run, type: Time
9
+
10
+ index({ path: 1})
11
+
12
+ def headers
13
+ %w{name active trigger status task1 task2 task3 task4 task5}
14
+ end
15
+
16
+ def last_cached_at
17
+ r = self
18
+ Dataset.find_or_create_by_path(r.path).last_cached_at
19
+ end
20
+
21
+ def worker
22
+ r = self
23
+ Mobilize::Resque.find_worker_by_path(r.path)
24
+ end
25
+
26
+ def Runner.find_by_email(email)
27
+ Runner.where(:email=>email).first
28
+ end
29
+
30
+ def Runner.find_by_path(path)
31
+ Runner.where(:path=>path).first
32
+ end
33
+
34
+ def Runner.perform(id,*args)
35
+ r = Runner.find_by_path(id)
36
+ #get gdrive slot for read
37
+ gdrive_slot = Gdrive.slot_worker_by_path(r.path)
38
+ unless gdrive_slot
39
+ r.update_status("no gdrive slot available")
40
+ return false
41
+ end
42
+ #make sure any updates to activity are processed first
43
+ #as in when someone runs a "once" job that has completed
44
+ r.update_gsheet(gdrive_slot)
45
+ #read the jobs in the gsheet and update models with news
46
+ r.read_gsheet(gdrive_slot)
47
+ #queue up the jobs that are due and active
48
+ r.jobs.each do |j|
49
+ begin
50
+ if j.is_due?
51
+ j.tasks.first.enqueue!
52
+ end
53
+ rescue ScriptError, StandardError => exc
54
+ j.update_status("Failed to enqueue with #{exc.to_s}")
55
+ j.update_attributes(:active=>false)
56
+ end
57
+ end
58
+ r.update_gsheet(gdrive_slot)
59
+ r.update_attributes(:last_run=>Time.now.utc)
60
+ end
61
+
62
+ def dataset
63
+ r = self
64
+ Dataset.find_or_create_by_handler_and_path("gsheet",r.path)
65
+ end
66
+
67
+ def Runner.find_or_create_by_path(path)
68
+ Runner.where(:path=>path).first || Runner.create(:path=>path,:active=>true)
69
+ end
70
+
71
+ def read_cache
72
+ r = self
73
+ r.dataset.read_cache
74
+ end
75
+
76
+ def gsheet(gdrive_slot)
77
+ r = self
78
+ jobs_sheet = Gsheet.find_or_create_by_path(r.path,gdrive_slot)
79
+ jobs_sheet.add_headers(r.headers)
80
+ jobs_sheet.delete_sheet1
81
+ return jobs_sheet
82
+ end
83
+
84
+ def read_gsheet(gdrive_slot)
85
+ r = self
86
+ gsheet_tsv = r.gsheet(gdrive_slot).to_tsv
87
+ #cache in DB
88
+ r.dataset.write_cache(gsheet_tsv)
89
+ #turn it into a hash array
90
+ gsheet_jobs = gsheet_tsv.tsv_to_hash_array
91
+ #go through each job, update relevant job with its params
92
+ done_jobs = []
93
+ #parse out the jobs and update the Job collection
94
+ gsheet_jobs.each_with_index do |rj,rj_i|
95
+ #skip non-jobs or jobs without required values
96
+ next if (rj['name'].to_s.first == "#" or ['name','active','trigger','task1'].select{|c| rj[c].to_s.strip==""}.length>0)
97
+ j = Job.find_or_create_by_path("#{r.path}/#{rj['name']}")
98
+ #update top line params
99
+ j.update_attributes(:active => rj['active'],
100
+ :trigger => rj['trigger'])
101
+ (1..5).to_a.each do |t_idx|
102
+ task_string = rj["task#{t_idx.to_s}"]
103
+ break if task_string.to_s.length==0
104
+ t = Task.find_or_create_by_path("#{j.path}/task#{t_idx.to_s}")
105
+ #parse command string, update task with it
106
+ t_handler, call, param_string = [""*3]
107
+ task_string.split(" ").ie do |spls|
108
+ t_handler = spls.first.split(".").first
109
+ call = spls.first.split(".").last
110
+ param_string = spls[1..-1].join(" ").strip
111
+ end
112
+ t.update_attributes(:call=>call, :handler=>t_handler, :param_string=>param_string)
113
+ end
114
+ r.update_status("Updated #{j.path} tasks at #{Time.now.utc}")
115
+ #add this job to list of read ones
116
+ done_jobs << j
117
+ end
118
+ #delete user jobs that are not included in Runner
119
+ (r.jobs.map{|j| j.path} - done_jobs.map{|j| j.path}).each do |rj_path|
120
+ j = Job.where(:path=>rj_path).first
121
+ j.delete if j
122
+ r.update_status("Deleted job:#{rj_path}")
123
+ end
124
+ r.update_status("jobs read at #{Time.now.utc}")
125
+ return true
126
+ end
127
+
128
+ def update_gsheet(gdrive_slot)
129
+ r = self
130
+ jobs_gsheet = r.gsheet(gdrive_slot)
131
+ upd_rows = r.jobs.map{|j| {'name'=>j.name, 'active'=>j.active, 'status'=>j.status} }
132
+ jobs_gsheet.add_or_update_rows(upd_rows)
133
+ r.update_status("gsheet updated")
134
+ return true
135
+ end
136
+
137
+ def jobs(jname=nil)
138
+ r = self
139
+ js = Job.where(:path=>/^#{r.path.escape_regex}/).to_a
140
+ if jname
141
+ return js.sel{|j| j.name == jname}.first
142
+ else
143
+ return js
144
+ end
145
+ end
146
+
147
+ def user
148
+ r = self
149
+ user_name = r.path.split("_").second.split("(").first.split("/").first
150
+ User.where(:email=>[user_name,Gdrive.domain].join("@")).first
151
+ end
152
+
153
+ def update_status(msg)
154
+ r = self
155
+ r.update_attributes(:status=>msg)
156
+ Mobilize::Resque.set_worker_args_by_path(r.path,{'status'=>msg})
157
+ return true
158
+ end
159
+
160
+ def is_working?
161
+ r = self
162
+ Mobilize::Resque.active_paths.include?(r.path)
163
+ end
164
+
165
+ def is_due?
166
+ r = self.reload
167
+ return false if r.is_working?
168
+ last_due_time = Time.now.utc - Jobtracker.runner_read_freq
169
+ return true if r.last_run.nil? or r.last_run < last_due_time
170
+ end
171
+
172
+ def enqueue!
173
+ r = self
174
+ ::Resque::Job.create("mobilize",Runner,r.path,{})
175
+ return true
176
+ end
177
+ end
178
+ end