summaryrefslogtreecommitdiff
path: root/application.rb
blob: 1cd32e0809f85bb5bf66469a86881ef0d98dca22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
require 'rubygems'
gem "opentox-ruby", "~> 0"
require 'opentox-ruby'

class Task
	include DataMapper::Resource
	property :id, Serial
	property :uri, String, :length => 255
  property :created_at, DateTime
  
  property :finished_at, DateTime
  property :due_to_time, DateTime
  property :pid, Integer
  
  property :resultURI, String, :length => 255
  property :percentageCompleted, Float, :default => 0
  property :hasStatus, String, :default => "Running" #possible states are: "Cancelled", "Completed", "Running", "Error"
  property :title, String, :length => 255
  property :creator, String, :length => 255
  property :description, Text

  def metadata
    {
      DC.creator => @creator,
      DC.title => @title,
      DC.date => @created_at,
      OT.hasStatus => @hasStatus,
      OT.resultURI => @resultURI,
      OT.percentageCompleted => @percentageCompleted,
      #text fields are lazy loaded, using member variable can cause description to be nil
      DC.description => description   
      #:due_to_time => @due_to_timer
    }
  end
end

DataMapper.auto_upgrade!

# Get a list of all tasks
# @return [text/uri-list] List of all tasks
get '/?' do
	LOGGER.debug "list all tasks "+params.inspect
  if request.env['HTTP_ACCEPT'] =~ /html/
    response['Content-Type'] = 'text/html'
    OpenTox.text_to_html Task.all(params).collect{|t| t.uri}.join("\n") + "\n"
  else
    response['Content-Type'] = 'text/uri-list'
    Task.all(params).collect{|t| t.uri}.join("\n") + "\n"
  end
end

# Get task representation
# @param [Header] Accept Mime type of accepted representation, may be one of `application/rdf+xml,application/x-yaml,text/uri-list`
# @return [application/rdf+xml,application/x-yaml,text/uri-list] Task representation in requested format, Accept:text/uri-list returns URI of the created resource if task status is "Completed"
get '/:id/?' do
  task = Task.get(params[:id])
  halt 404, "Task '#{params[:id]}' not found." unless task
  code = task.hasStatus == "Running" ? 202 : 200
  
  case request.env['HTTP_ACCEPT']
  when /yaml/ 
    response['Content-Type'] = 'application/x-yaml'
    halt code, task.metadata.to_yaml
  when /html/
    response['Content-Type'] = 'text/html'
    halt code, OpenTox.text_to_html(task.metadata.to_yaml)    
  when /application\/rdf\+xml|\*\/\*/ # matches 'application/x-yaml', '*/*'
    response['Content-Type'] = 'application/rdf+xml'
    t = OpenTox::Task.new task.uri
    t.add_metadata task.metadata
    halt code, t.to_rdfxml
  when /text\/uri\-list/
    response['Content-Type'] = 'text/uri-list'
    halt code, task.resultURI
  else
    halt 400, "MIME type '"+request.env['HTTP_ACCEPT'].to_s+"' not supported, valid Accept-Headers are \"application/rdf+xml\" and \"application/x-yaml\"."
  end
end

# Get Task properties. Works for
# - /task/id
# - /task/uri
# - /task/created_at
# - /task/finished_at
# - /task/due_to_time
# - /task/pid
# - /task/resultURI
# - /task/percentageCompleted
# - /task/hasStatus
# - /task/title
# - /task/creator
# - /task/description
# @return [String] Task property
get '/:id/:property/?' do
	response['Content-Type'] = 'text/plain'
  task = Task.get(params[:id])
  halt 404,"Task #{params[:id]} not found." unless task
  begin
    eval("task.#{params[:property]}").to_s
  rescue
    halt 404,"Unknown task property #{params[:property]}."
  end
end

# Create a new task
# @param [optional,String] max_duration
# @param [optional,String] pid
# @param [optional,String] resultURI
# @param [optional,String] percentageCompleted
# @param [optional,String] hasStatus
# @param [optional,String] title
# @param [optional,String] creator
# @param [optional,String] description
# @return [text/uri-list] URI for new task
post '/?' do
  LOGGER.debug "Creating new task with params "+params.inspect
  max_duration = params.delete(:max_duration.to_s) if params.has_key?(:max_duration.to_s)
  task = Task.create(params)
  task.uri = url_for("/#{task.id}", :full)
  task.due_to_time = DateTime.parse((Time.parse(task.created_at.to_s) + max_duration.to_f).to_s) if max_duration
  raise "Could not save task #{task.uri}" unless task.save
  response['Content-Type'] = 'text/uri-list'
  task.uri + "\n"
end

# Change task status. Possible URIs are: `
# - /task/Cancelled
# - /task/Completed: requires taskURI argument
# - /task/Running
# - /task/Error
# - /task/pid: requires pid argument
# IMPORTANT NOTE: Rack does not accept empty PUT requests. Please send an empty parameter (e.g. with -d '' for curl) or you will receive a "411 Length Required" error.
# @param [optional, String] resultURI URI of created resource, required for /task/Completed
# @param [optional, String] pid Task PID, required for /task/pid
# @param [optional, String] description Task description
# @param [optional, String] percentageCompleted progress value, can only be set while running
# @return [] nil
put '/:id/:hasStatus/?' do
  
	task = Task.get(params[:id])
  halt 404,"Task #{params[:id]} not found." unless task
	task.hasStatus = params[:hasStatus] unless /pid/ =~ params[:hasStatus]
  task.description = params[:description] if params[:description]
  
	case params[:hasStatus]
	when "Completed"
		LOGGER.debug "Task " + params[:id].to_s + " completed"
    halt 402,"no param resultURI when completing task" unless params[:resultURI]
    task.resultURI = params[:resultURI]
		task.finished_at = DateTime.now
    task.percentageCompleted = 100
    task.pid = nil
  when "pid"
    task.pid = params[:pid]
  when "Running"
    halt 400,"Task cannot be set to running after not running anymore" if task.hasStatus!="Running"
    task.percentageCompleted = params[:percentageCompleted].to_f
    LOGGER.debug "Task " + params[:id].to_s + " set percentage completed to: "+params[:percentageCompleted].to_s
	when /Cancelled|Error/
		Process.kill(9,task.pid) unless task.pid.nil?
		task.pid = nil
  else
     halt 402,"Invalid value for hasStatus: '"+params[:hasStatus].to_s+"'"
  end
	
  halt 500,"could not save task" unless task.save

end

# Delete a task
# @return [text/plain] Status message
delete '/:id/?' do
	task = Task.get(params[:id])
  halt 404, "Task #{params[:id]} not found." unless task
	begin
		Process.kill(9,task.pid) unless task.pid.nil?
	rescue
		halt 500,"Cannot kill task with pid #{task.pid}"
	end
	task.destroy!
	response['Content-Type'] = 'text/plain'
	"Task #{params[:id]} deleted."
end

# Delete all tasks
# @return [text/plain] Status message
delete '/?' do
	Task.all.each do |task|
		begin
			Process.kill(9,task.pid) unless task.pid.nil?
		rescue
			"Cannot kill task with pid #{task.pid}"
		end
		#task.destroy!
	end
  Task.auto_migrate!
	response['Content-Type'] = 'text/plain'
	"All tasks deleted."
end