dvdplm-taskr 0.3.1

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,338 @@
1
+ # This file is part of Taskr.
2
+ #
3
+ # Taskr is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Taskr is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module Taskr::Controllers
17
+
18
+ class ActionTypes < REST 'action_types'
19
+ def list
20
+ @actions = Taskr::Actions.list
21
+
22
+ render :action_list
23
+ end
24
+ end
25
+
26
+ class Actions < REST 'actions'
27
+ def parameters_form(id)
28
+ @num = @input[:num] || 0
29
+ @action = Taskr::Actions.list.find {|a| a.to_s =~ Regexp.new("#{id}$")}
30
+ if @action
31
+ render :action_parameters_form
32
+ else
33
+ @status = 404
34
+ "Action #{id.inspect} not defined"
35
+ end
36
+ end
37
+
38
+ def new
39
+ @num = @input[:num] || 0
40
+ @actions = Taskr::Actions.list
41
+ render :action_form
42
+ end
43
+ end
44
+
45
+ class Tasks < REST 'tasks'
46
+ include Taskr::Models
47
+
48
+ # List of tasks.
49
+ def list
50
+ @tasks = Task.find(:all, :include => [:task_actions])
51
+
52
+ render :tasks_list
53
+ end
54
+
55
+ # Input for a new task.
56
+ def new
57
+ @actions = Taskr::Actions.list
58
+
59
+ render :new_task
60
+ end
61
+
62
+ # Retrieve details for an existing task.
63
+ def read(id)
64
+ @task = Task.find(id, :include => [:task_actions])
65
+
66
+ render :view_task
67
+ end
68
+
69
+ def edit(task_id)
70
+ @task = Task.find(task_id, :include => [:task_actions])
71
+ @actions = Taskr::Actions.list
72
+ render :edit_task
73
+ end
74
+
75
+ def update(task_id)
76
+ $LOG.debug "Update Input params: #{@input.inspect}"
77
+ @task = Task.find(task_id, :include => [:task_actions])
78
+ params = normalize_input(@input)
79
+ @task.attributes= {
80
+ :name => params[:name],
81
+ :schedule_method => params[:schedule_method],
82
+ :schedule_when => params[:schedule_when],
83
+ :memo => params[:memo]
84
+ }
85
+
86
+ @task.task_actions.each do |action|
87
+ $LOG.debug("Updating parameters for #{action.inspect}")
88
+ action_params = params[:action].delete("action_id_#{action.id}")
89
+ $LOG.debug("Using values #{action_params.inspect}")
90
+ next unless action_params
91
+ action_params.each do |param_name, value|
92
+ $LOG.debug("Looking up \"#{param_name}\". Setting value to \"#{value}\"")
93
+ action.parameters.find_by_name(param_name).update_attribute(:value, value)
94
+ end
95
+ end
96
+
97
+ # Create new actions/action_parameters for the remaining params
98
+ unless params[:action].empty?
99
+ params[:action].map do |num, params|
100
+ $LOG.debug "Looping remaining action_parameters: #{params.inspect} (num: #{num})"
101
+ action_class = get_action_class(params[:action_class_name])
102
+ action = TaskAction.new(:order => params[:order] || (@task.task_actions.maximum(:order)+1) || num, :action_class_name => action_class.to_s)
103
+
104
+ action_class.parameters.each do |p|
105
+ value = params[p]
106
+ value = nil if value.blank?
107
+ action.action_parameters << TaskActionParameter.new(:name => p, :value => value)
108
+ end
109
+ @task.task_actions << action
110
+ end
111
+ end
112
+
113
+ unless @task.valid?
114
+ @status = 500
115
+ @actions = Taskr::Actions.list
116
+ else
117
+ @task.save!
118
+ @status = 200
119
+ end
120
+
121
+ @task.reload # Ensure any updates to the record goes in
122
+
123
+ Taskr.scheduler.unschedule(@task.scheduler_job_id)
124
+ @task.schedule! Taskr.scheduler
125
+
126
+ $LOG.info "Task \"#{@task.name}\" (ID: #{@task.id}) updated sucessfully."
127
+ redirect R(@task)
128
+ end
129
+
130
+ def normalize_input(hsh)
131
+ hsh[:task] || hsh["0"] || hsh
132
+ end
133
+
134
+ def normalize_actions_params(input_params)
135
+ $LOG.debug "normalize_actions_params Normalizing: #{input_params.inspect}"
136
+ # some gymnastics here to provide compatibility for the way various
137
+ # REST client libraries submit data
138
+ actions_data = input_params[:actions] || input_params[:action]
139
+
140
+ raise ArgumentError, "Missing action(s) parameter." if actions_data.blank?
141
+ actions = case actions_data
142
+ when Array
143
+ $LOG.debug "normalize_actions_params Plain Array. Returning as-is."
144
+ actions_data
145
+ when Hash
146
+ $LOG.debug "normalize_actions_params Some weird Hash. Injecting."
147
+ actions_data.inject([]) do |acc,(i,a)|
148
+ $LOG.debug "normalize_actions_params acc: #{acc.inspect} index: #{i.inspect} array: #{a.inspect}."
149
+ acc << a
150
+ acc
151
+ end
152
+ else
153
+ $LOG.debug "normalize_actions_params Not a weird hash.\n\tactions_data[:action]: #{actions_data[:action].inspect}\n\tactions_data[:actions]: #{actions_data[:actions].inspect}\n\tactions_data: #{actions_data.inspect}\n\n"
154
+ actions_data[:action] || actions_data[:actions] || actions_data
155
+ end
156
+ actions = [actions] unless actions.kind_of? Array
157
+ $LOG.debug "normalize_actions_params DONE. Returning: #{actions.inspect}"
158
+ actions
159
+ end
160
+
161
+ def get_action_class(class_name)
162
+ action_class_name = "Taskr::Actions::#{class_name}" unless class_name =~ /^Taskr::Actions::/
163
+
164
+ begin
165
+ action_class = action_class_name.constantize
166
+ unless action_class.include? Rufus::Schedulable
167
+ raise ArgumentError,
168
+ "#{a[:action_class_name].inspect} cannot be used as an action because it does not include the Rufus::Schedulable module."
169
+ end
170
+ rescue NameError
171
+ raise ArgumentError,
172
+ "#{a[:action_class_name].inspect} is not defined (i.e. there is no such action class)."
173
+ end
174
+ action_class
175
+ end
176
+
177
+ # Create and schedule a new task.
178
+ def create
179
+ puts @input.inspect
180
+ begin
181
+ # the "0" is for compatibility with PHP's Zend_Rest_Client
182
+ task_data = @input[:task] || @input["0"] || @input
183
+
184
+ name = task_data[:name]
185
+ created_by = @env['REMOTE_HOST']
186
+ schedule_method = task_data[:schedule_method]
187
+ schedule_when = task_data[:schedule_when]
188
+ memo = task_data[:memo]
189
+
190
+ @task = Task.new(
191
+ :name => name,
192
+ :created_by => created_by,
193
+ :schedule_method => schedule_method,
194
+ :schedule_when => schedule_when,
195
+ :memo => memo
196
+ )
197
+
198
+ # some gymnastics here to provide compatibility for the way various
199
+ # REST client libraries submit data
200
+ actions_data = task_data[:actions] || task_data[:action]
201
+
202
+ raise ArgumentError, "Missing action(s) parameter." if actions_data.blank?
203
+
204
+ if actions_data.kind_of?(Array)
205
+ actions = actions_data
206
+ elsif actions_data["0"]
207
+ actions = []
208
+ actions_data.each do |i,a|
209
+ actions << a
210
+ end
211
+ else
212
+ actions = actions_data[:action] || actions_data[:actions] || actions_data
213
+ end
214
+
215
+ actions = [actions] unless actions.kind_of? Array
216
+ #puts actions.inspect
217
+
218
+ i = 0
219
+ actions.each do |a|
220
+ #puts a.inspect
221
+ action_class_name = a[:action_class_name]
222
+ action_class_name = "Taskr::Actions::#{action_class_name}" unless action_class_name =~ /^Taskr::Actions::/
223
+
224
+ begin
225
+ action_class = action_class_name.constantize
226
+ unless action_class.include? Rufus::Schedulable
227
+ raise ArgumentError,
228
+ "#{a[:action_class_name].inspect} cannot be used as an action because it does not include the Rufus::Schedulable module."
229
+ end
230
+ rescue NameError
231
+ raise ArgumentError,
232
+ "#{a[:action_class_name].inspect} is not defined (i.e. there is no such action class)."
233
+ end
234
+
235
+ action = TaskAction.new(:order => a[:order] || i, :action_class_name => action_class_name)
236
+
237
+
238
+ action_class.parameters.each do |p|
239
+ value = a[p]
240
+ value = nil if value.blank?
241
+ action.action_parameters << TaskActionParameter.new(:name => p, :value => value)
242
+ end
243
+
244
+ @task.task_actions << action
245
+ i += 1
246
+ end
247
+
248
+
249
+ unless @task.valid?
250
+ @status = 500
251
+ @actions = Taskr::Actions.list
252
+ return render(:new_task)
253
+ end
254
+
255
+
256
+ @task.schedule! Taskr.scheduler
257
+
258
+ if @task.save
259
+ location = "/tasks/#{@task.id}?format=#{@format}"
260
+ $LOG.debug "#{@task} saved successfuly. Setting Location header to #{location.inspect}."
261
+ @headers['Location'] = location
262
+ end
263
+
264
+ return render(:view_task)
265
+ rescue => e
266
+ puts e.inspect
267
+ puts e.backtrace
268
+ raise e
269
+ end
270
+ end
271
+
272
+ def run(id)
273
+ @task = Task.find(id, :include => [:task_actions])
274
+
275
+ action = @task.prepare_action
276
+
277
+ LogEntry.info(@task, "Manually executing task #{@task}.")
278
+
279
+ begin
280
+ action.trigger
281
+ rescue
282
+ # ok to catch exception silently. it should have gotten logged by the action
283
+ end
284
+
285
+ redirect R(@task)
286
+ end
287
+
288
+ # Unschedule and delete an existing task.
289
+ def destroy(id)
290
+ @task = Task.find(id)
291
+ if @task.scheduler_job_id
292
+ $LOG.debug "Unscheduling task #{@task}..."
293
+ Taskr.scheduler.unschedule(@task.scheduler_job_id)
294
+ end
295
+ @task.destroy
296
+
297
+ if @task.frozen?
298
+ @status = 200
299
+ if @format == :XML
300
+ ""
301
+ else
302
+ return redirect(R(Tasks, :list))
303
+ end
304
+ else
305
+ _error("Task #{id} was not destroyed.", 500)
306
+ end
307
+ end
308
+
309
+ # Reload a task
310
+ def reload(id)
311
+ @task = Task.find(id)
312
+ $LOG.debug "Re-scheduling task #{@task}..."
313
+ if @task.scheduler_job_id
314
+ Taskr.scheduler.unschedule(@task.scheduler_job_id)
315
+ $LOG.debug "\t...unscheduled task #{@task}..."
316
+ end
317
+ @task.schedule! Taskr.scheduler
318
+ $LOG.debug "\t...scheduled task #{@task}...\n"
319
+ redirect R(@task)
320
+ end
321
+ end
322
+
323
+ class LogEntries < REST 'log_entries'
324
+ def list
325
+ @since = @input[:since]
326
+
327
+ @level = ['DEBUG', 'INFO', 'WARN', 'ERROR']
328
+ @level.index(@input[:level]).times {@level.shift} if @input[:level]
329
+
330
+ @log_entries = LogEntry.find(:all,
331
+ :conditions => ['task_id = ? AND IF(?,timestamp > ?,1) AND level IN (?)',
332
+ @input[:task_id], !@since.blank?, @since, @level],
333
+ :order => 'timestamp DESC, id DESC')
334
+
335
+ render :log_entries_list
336
+ end
337
+ end
338
+ end
@@ -0,0 +1,48 @@
1
+ # This file is part of Taskr.
2
+ #
3
+ # Taskr is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Taskr is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ $: << File.dirname(File.expand_path(__FILE__))
17
+
18
+ # Try to load local versions of Picnic and Reststop if possible...
19
+ $: << File.dirname(File.expand_path(__FILE__))+"/../../../picnic/lib"
20
+ $: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/picnic/lib"
21
+ $: << File.dirname(File.expand_path(__FILE__))+"/../../../reststop/lib"
22
+ $: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/reststop/lib"
23
+
24
+ # active_resource needs newer versions of active_support, but this conflicts
25
+ # with active_record, so we need a newer version of that as well (yes, it's a mess)
26
+ #$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activeresource/lib"
27
+ #$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activesupport/lib"
28
+ #$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activerecord/lib"
29
+
30
+ require 'rubygems'
31
+
32
+ require 'active_support'
33
+ #require 'active_resource'
34
+ require 'active_record'
35
+
36
+
37
+ # make things backwards-compatible for rubygems < 0.9.0
38
+ unless Object.method_defined? :gem
39
+ alias gem require_gem
40
+ end
41
+
42
+ require 'picnic'
43
+ require 'camping/db'
44
+
45
+ require 'reststop'
46
+
47
+ gem 'rufus-scheduler', '~> 1.0.7'
48
+ require 'rufus/scheduler'
@@ -0,0 +1,79 @@
1
+ # This file is part of Taskr.
2
+ #
3
+ # Taskr is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Taskr is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module Taskr::Helpers
17
+ def taskr_response_xml(result, &block)
18
+ instruct!
19
+ tag!("response", 'result' => result, 'xmlns:taskr' => "http://taskr.googlecode.com") do
20
+ yield
21
+ end
22
+ end
23
+
24
+ def html_task_action_li(ta)
25
+ li ta.action_class_name
26
+ ul do
27
+ ta.action_parameters.each do |ap|
28
+ li do
29
+ label "#{ap.name}:"
30
+ pre ap.value
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def html_scaffold
37
+ html do
38
+ head do
39
+ title "Taskr"
40
+ link(:rel => 'stylesheet', :type => 'text/css', :href => '/public/taskr.css')
41
+ script(:type => 'text/javascript', :src => '/public/prototype.js')
42
+ end
43
+ body do
44
+ yield
45
+ end
46
+ end
47
+ end
48
+
49
+ # Taken from Rails
50
+ def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
51
+ from_time = from_time.to_time if from_time.respond_to?(:to_time)
52
+ to_time = to_time.to_time if to_time.respond_to?(:to_time)
53
+ distance_in_minutes = (((to_time - from_time).abs)/60).round
54
+ distance_in_seconds = ((to_time - from_time).abs).round
55
+
56
+ case distance_in_minutes
57
+ when 0..1
58
+ return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds
59
+ case distance_in_seconds
60
+ when 0..4 then 'less than 5 seconds'
61
+ when 5..9 then 'less than 10 seconds'
62
+ when 10..19 then 'less than 20 seconds'
63
+ when 20..39 then 'half a minute'
64
+ when 40..59 then 'less than a minute'
65
+ else '1 minute'
66
+ end
67
+
68
+ when 2..44 then "#{distance_in_minutes} minutes"
69
+ when 45..89 then 'about 1 hour'
70
+ when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
71
+ when 1440..2879 then '1 day'
72
+ when 2880..43199 then "#{(distance_in_minutes / 1440).round} days"
73
+ when 43200..86399 then 'about 1 month'
74
+ when 86400..525959 then "#{(distance_in_minutes / 43200).round} months"
75
+ when 525960..1051919 then 'about 1 year'
76
+ else "over #{(distance_in_minutes / 525960).round} years"
77
+ end
78
+ end
79
+ end