cpee-worklist 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +3 -0
- data/LICENSE +165 -0
- data/README.md +21 -0
- data/Rakefile +21 -0
- data/cpee-worklist.gemspec +27 -0
- data/lib/cpee-worklist/activities.rb +36 -0
- data/lib/cpee-worklist/controller.rb +160 -0
- data/lib/cpee-worklist/implementation.rb +432 -0
- data/lib/cpee-worklist/implementation.xml +35 -0
- data/lib/cpee-worklist/organisation.rng +82 -0
- data/lib/cpee-worklist/routing/end.rb +59 -0
- data/lib/cpee-worklist/routing/forward-events.rb +78 -0
- data/lib/cpee-worklist/routing/forward-votes.rb +125 -0
- data/lib/cpee-worklist/routing/persist.rb +75 -0
- data/lib/cpee-worklist/topics.xml +32 -0
- data/lib/cpee-worklist/user.rb +61 -0
- data/lib/cpee-worklist/utils.rb +52 -0
- data/lib/cpee-worklist/wlengine.xml +176 -0
- data/server/worklist +37 -0
- data/server/worklist.conf +2 -0
- data/tools/cpee-worklist +49 -0
- metadata +135 -0
@@ -0,0 +1,432 @@
|
|
1
|
+
# This file is part of CPEE-WORKLIST
|
2
|
+
#
|
3
|
+
# CPEE-WORKLIST is free software: you can redistribute it and/or modify it
|
4
|
+
# under the terms of the GNU Lesser General Public License as published by the
|
5
|
+
# Free Software Foundation, either version 3 of the License, or (at your
|
6
|
+
# option) any later version.
|
7
|
+
#
|
8
|
+
# CPEE-WORKLIST is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
10
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
11
|
+
# details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public License
|
14
|
+
# along with CPEE-WORKLIST (file LICENSE in the main directory). If not, see
|
15
|
+
# <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'pp'
|
18
|
+
require 'json'
|
19
|
+
require 'fileutils'
|
20
|
+
require 'rubygems'
|
21
|
+
require 'riddl/server'
|
22
|
+
require 'riddl/client'
|
23
|
+
require 'riddl/utils/notifications_producer'
|
24
|
+
require 'riddl/utils/fileserve'
|
25
|
+
require 'cpee/redis'
|
26
|
+
require 'cpee/message'
|
27
|
+
require 'cpee/persistence'
|
28
|
+
require 'cpee/attributes_helper'
|
29
|
+
require 'cpee/implementation_notifications'
|
30
|
+
require 'cpee/implementation_callbacks'
|
31
|
+
require 'chronic_duration'
|
32
|
+
|
33
|
+
require_relative 'activities'
|
34
|
+
require_relative 'controller'
|
35
|
+
require_relative 'utils'
|
36
|
+
require_relative 'user'
|
37
|
+
|
38
|
+
CPEE::Message::who = 'cpee-worklist'
|
39
|
+
CPEE::Message::type = 'worklist'
|
40
|
+
|
41
|
+
module CPEE
|
42
|
+
module Worklist
|
43
|
+
|
44
|
+
SERVER = File.expand_path(File.join(__dir__,'implementation.xml'))
|
45
|
+
|
46
|
+
class GetStatus < Riddl::Implementation #{{{
|
47
|
+
def response
|
48
|
+
status = File.read(File.join(@a[0],'users','status.txt')) rescue 'Currently no tasks available.'
|
49
|
+
Riddl::Parameter::Simple.new("status",status)
|
50
|
+
end
|
51
|
+
end #}}}
|
52
|
+
|
53
|
+
class SetStatus < Riddl::Implementation #{{{
|
54
|
+
def response
|
55
|
+
@a[0].notify('user/status', { 'status' => @p[0].value })
|
56
|
+
File.write(File.join(@a[1],'users','status.txt'),@p[0].value)
|
57
|
+
end
|
58
|
+
end #}}}
|
59
|
+
|
60
|
+
class ActivityHappens < Riddl::Implementation #{{{
|
61
|
+
def response
|
62
|
+
controller = @a[0]
|
63
|
+
|
64
|
+
activity = {}
|
65
|
+
activity['process'] = @h.keys.include?('CPEE_ATTR_INFO') ? "#{@h['CPEE_ATTR_INFO']} (#{@h['CPEE_INSTANCE'].split('/').last})" : "DUMMY PROCESS (#{@h['CPEE_INSTANCE'].split('/').last})"
|
66
|
+
activity['label'] = @h.keys.include?('CPEE_INSTANCE') ? "#{@h['CPEE_LABEL']}" : 'DUMMY LABEL'
|
67
|
+
activity['user'] = []
|
68
|
+
activity['url'] = @h['CPEE_CALLBACK']
|
69
|
+
activity['id'] = @h['CPEE_CALLBACK_ID']
|
70
|
+
|
71
|
+
activity['cpee_activity_id'] = @h['CPEE_ACTIVITY']
|
72
|
+
activity['cpee_base'] = @h['CPEE_BASE']
|
73
|
+
activity['cpee_instance'] = @h['CPEE_INSTANCE']
|
74
|
+
|
75
|
+
activity['uuid'] = @h['CPEE_ATTR_UUID']
|
76
|
+
|
77
|
+
omo = @p.shift.value
|
78
|
+
activity['orgmodel'] = @h[ 'CPEE_ATTR_' + omo.upcase] || omo
|
79
|
+
|
80
|
+
activity['form'] = @p.shift.value
|
81
|
+
activity['unit'] = @p.first.name == 'unit' ? @p.shift.value : '*'
|
82
|
+
activity['role'] = @p.first.name == 'role' ? @p.shift.value : '*'
|
83
|
+
activity['priority'] = @p.first.name == 'priority' ? @p.shift.value.to_i : 1
|
84
|
+
activity['collect'] = @p.first.name == 'collect' ? @p.shift.value.to_i : nil
|
85
|
+
activity['deadline'] = @p.first.name == 'deadline' ? ((Time.now + ChronicDuration.parse(@p.shift.value)) rescue nil): nil
|
86
|
+
activity['restrictions'] = []
|
87
|
+
rests = JSON::parse(@p.shift.value) rescue nil
|
88
|
+
activity['restrictions'] << rests unless rests.nil?
|
89
|
+
activity['parameters'] = JSON::parse(@p.shift.value) rescue {}
|
90
|
+
status, content, headers = Riddl::Client.new(activity['orgmodel']).get
|
91
|
+
if status == 200
|
92
|
+
begin
|
93
|
+
xml = content[0].value.read
|
94
|
+
schema = XML::Smart.open(@a[0].opts[:ORG_SCHEMA])
|
95
|
+
org_xml = XML::Smart.string(xml)
|
96
|
+
raise 'a fucked up xml (wink wink)' unless org_xml.validate_against(schema)
|
97
|
+
org_xml.register_namespace 'o', 'http://cpee.org/ns/organisation/1.0'
|
98
|
+
rescue => e
|
99
|
+
puts e.message
|
100
|
+
puts e.backtrace
|
101
|
+
@a[0].notify('task/invalid', :callback_id => activity['id'], :reason => 'orgmodel invalid', :instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'] )
|
102
|
+
@status = 404
|
103
|
+
return
|
104
|
+
end
|
105
|
+
attributes = ""
|
106
|
+
if activity['role'] != '*'
|
107
|
+
attributes += "@role='#{activity['role']}'"
|
108
|
+
attributes += " and " if activity['unit'] != '*'
|
109
|
+
end
|
110
|
+
attributes += "@unit='#{activity['unit']}'" if activity['unit'] != '*'
|
111
|
+
user = org_xml.find("/o:organisation/o:subjects/o:subject[o:relation[#{attributes}]]").map{ |e| e.attributes['uid'] }
|
112
|
+
|
113
|
+
if activity['collect']
|
114
|
+
activity['collect_max'] = user.length
|
115
|
+
activity['collected'] = 0
|
116
|
+
end
|
117
|
+
|
118
|
+
if user.empty?
|
119
|
+
@a[0].notify('task/invalid', :callback_id => activity['id'], :reason => 'no users found for this combination of unit/role',:instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'] )
|
120
|
+
@status = 404
|
121
|
+
return
|
122
|
+
end
|
123
|
+
@a[0].add_activity activity
|
124
|
+
@a[0].add_orgmodel Riddl::Protocols::Utils::escape(activity['orgmodel']), xml
|
125
|
+
Thread.new do
|
126
|
+
# TODO immediate vote for adding by external subscribers
|
127
|
+
# results = @a[0].vote('task/add', :user => user , :instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'] )
|
128
|
+
# if (results.length == 1) && (user.include? results[0])
|
129
|
+
# activity['user'] = results[0]
|
130
|
+
# info = CPEE::Worklist::User::info(@a[0].opts,activity,activity['user'])
|
131
|
+
# @a[0].notify('task/add', :user => user,:callback_id => activity['id'], :instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'], :wl_instance => activity['wl_instance'] )
|
132
|
+
# @a[0].notify('user/take', :user => results[0], :callback_id => activity['id'], :instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'], :organisation => info, :wl_instance => activity['wl_instance'])
|
133
|
+
# else
|
134
|
+
@a[0].notify('task/add', :user => user,:callback_id => activity['id'], :instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'])
|
135
|
+
# end
|
136
|
+
end
|
137
|
+
@headers << Riddl::Header.new('CPEE_CALLBACK','true')
|
138
|
+
else
|
139
|
+
@status = 404
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end #}}}
|
143
|
+
|
144
|
+
class TaskDel < Riddl::Implementation #{{{
|
145
|
+
def response
|
146
|
+
index = @a[0].activities.index{ |e| e["id"] == @r.last }
|
147
|
+
if index
|
148
|
+
activity = @a[0].activities[index]
|
149
|
+
if activity['collected'] && (activity['collected'] + 1) < activity['collect_max']
|
150
|
+
activity['collected'] += 1
|
151
|
+
activity['restrictions'] << { "restriction" => { "mode" => "prohibit", "id" => @r[-3] } }
|
152
|
+
@a[0].activities.serialize
|
153
|
+
@a[0].notify('user/finish', :callback_id => activity['id'], :user => @r[-3], :role => activity['role'],:instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'])
|
154
|
+
else
|
155
|
+
activity = @a[0].activities.delete_at(index)
|
156
|
+
@a[0].activities.serialize
|
157
|
+
if @r.length == 3
|
158
|
+
@a[0].notify('task/delete', :callback_id => activity['id'], :instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'])
|
159
|
+
Riddl::Client.new(activity['url']).put
|
160
|
+
else
|
161
|
+
info = CPEE::Worklist::User::info(@a[0].opts,activity,@r[-3])
|
162
|
+
@a[0].notify('user/finish', :callback_id => activity['id'], :user => @r[-3], :role => activity['role'],:instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'])
|
163
|
+
end
|
164
|
+
end
|
165
|
+
else
|
166
|
+
@status = 404
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end #}}}
|
170
|
+
|
171
|
+
class ShowTasks < Riddl::Implementation #{{{
|
172
|
+
def response
|
173
|
+
out = XML::Smart.string('<tasks/>')
|
174
|
+
umodels = @a[0].orgmodels.map do |fname|
|
175
|
+
doc = XML::Smart.open_unprotected(File.join(@a[0].opts[:top],'orgmodels',fname))
|
176
|
+
doc.register_namespace 'o', 'http://cpee.org/ns/organisation/1.0'
|
177
|
+
doc
|
178
|
+
end
|
179
|
+
@a[0].activities.each do |activity|
|
180
|
+
x = out.root.add "task", :callback_id => activity['id'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :instance_uuid => activity['uuid'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel']
|
181
|
+
x.add "process" , activity['process']
|
182
|
+
x.add "label" , activity['label']
|
183
|
+
x.add "role" , activity['role']
|
184
|
+
x.add "unit" , activity['unit']
|
185
|
+
|
186
|
+
if activity['user'].any?
|
187
|
+
umodels.each do |doc|
|
188
|
+
activity['user'].each do |user|
|
189
|
+
if user = doc.find("/o:organisation/o:subjects/o:subject[@uid='#{user}']").first
|
190
|
+
x.add "user", user.attributes['id'], :uid => user.attributes['uid']
|
191
|
+
break
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
else
|
196
|
+
xpath = ''
|
197
|
+
xpath = "[@role='#{activity['role']}' and @unit='#{activity['unit']}']" if (activity['unit'] != '*' && activity['role'] != '*' )
|
198
|
+
xpath = "[@role='#{activity['role']}']" if (activity['unit'] == '*' && activity['role'] != '*' )
|
199
|
+
xpath = "[@unit='#{activity['unit']}']" if (activity['unit'] != '*' && activity['role'] == '*' )
|
200
|
+
|
201
|
+
umodels.each do |doc|
|
202
|
+
if (tmp = doc.find("/o:organisation/o:subjects/o:subject[o:relation#{xpath}]")).length > 0
|
203
|
+
tmp.each{|e| x.add "user", e.attributes['id'], :uid => e.attributes['uid'] }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
Riddl::Parameter::Complex.new("tasks","text/xml", out.to_s)
|
209
|
+
end
|
210
|
+
end #}}}
|
211
|
+
|
212
|
+
class ShowUserTasks < Riddl:: Implementation #{{{
|
213
|
+
def response
|
214
|
+
out = XML::Smart.string('<tasks/>')
|
215
|
+
tasks = {}
|
216
|
+
@a[0].orgmodels.each do |e|
|
217
|
+
XML::Smart.open(File.join(@a[0].opts[:top],'orgmodels',e)) do |doc|
|
218
|
+
doc.register_namespace 'o', 'http://cpee.org/ns/organisation/1.0'
|
219
|
+
doc.find("/o:organisation/o:subjects/o:subject[@uid='#{@r[-2]}']/o:relation").each do |rel|
|
220
|
+
@a[0].activities.each do |activity|
|
221
|
+
restrict = false
|
222
|
+
activity['restrictions'].each do |restriction|
|
223
|
+
restrict = true if restriction['restriction']['mode'] == 'prohibit' && restriction['restriction']['id'] == @r[-2]
|
224
|
+
end
|
225
|
+
if (
|
226
|
+
activity['role']=='*' ||
|
227
|
+
activity['role'].casecmp(rel.attributes['role']) == 0
|
228
|
+
) && (
|
229
|
+
activity['unit'] == '*' ||
|
230
|
+
activity['unit'].casecmp(rel.attributes['unit']) == 0
|
231
|
+
) && (
|
232
|
+
activity['collect'] ||
|
233
|
+
activity['user'].empty? ||
|
234
|
+
activity['user'].include?(@r[-2])
|
235
|
+
) && !restrict
|
236
|
+
tasks["#{activity['id']}"] = { :all => activity.has_key?('collect') && !activity['collect'].nil?, :uid => @r[-2], :priority => activity['priority'], :label => activity['process'] + ': ' + activity['label'] }
|
237
|
+
tasks["#{activity['id']}"][:deadline] = activity['deadline'] if activity['deadline']
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
tasks.sort_by{ |k,e| e[:priority] }.each{|k,v| out.root.add("task", v.merge(:id => k))}
|
244
|
+
x = Riddl::Parameter::Complex.new("return","text/xml") do
|
245
|
+
out.to_s
|
246
|
+
end
|
247
|
+
x
|
248
|
+
end
|
249
|
+
end #}}}
|
250
|
+
|
251
|
+
class TaskTake < Riddl::Implementation #{{{
|
252
|
+
def response
|
253
|
+
index = @a[0].activities.index{ |c| c["id"] == @r.last }
|
254
|
+
if index
|
255
|
+
activity = @a[0].activities[index]
|
256
|
+
activity['user'].push @r[-3]if CPEE::Worklist::User::ok?(@a[0].opts,activity,@r[-3])
|
257
|
+
info = CPEE::Worklist::User::info(@a[0].opts,activity,@r[-3])
|
258
|
+
@a[0].activities.serialize
|
259
|
+
@a[0].notify('user/take', :user => @r[-3], :callback_id => activity['id'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'],:instance_uuid => activity['uuid'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'], :organisation => info)
|
260
|
+
Riddl::Client.new(@a[0].activities[index]['url']).put [
|
261
|
+
Riddl::Header.new('CPEE-UPDATE','true'),
|
262
|
+
Riddl::Header.new('CPEE-STATUS','take'),
|
263
|
+
Riddl::Header.new('CPEE-EVENT','take')
|
264
|
+
]
|
265
|
+
else
|
266
|
+
@status = 404
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end #}}}
|
270
|
+
|
271
|
+
class TaskGiveBack < Riddl::Implementation #{{{
|
272
|
+
def response
|
273
|
+
index = @a[0].activities.index{ |c| c["id"] == @r.last }
|
274
|
+
if index && (@a[0].activities[index]['user'] == @r[-3])
|
275
|
+
activity = @a[0].activities[index]
|
276
|
+
activity['user'] = []
|
277
|
+
callback_id = @a[0].activities[index]['id']
|
278
|
+
@a[0].activities.serialize
|
279
|
+
@a[0].notify('user/giveback', :callback_id => activity['id'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'],:instance_uuid => activity['uuid'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'])
|
280
|
+
Riddl::Client.new(@a[0].activities[index]['url']).put [
|
281
|
+
Riddl::Header.new('CPEE-UPDATE','true'),
|
282
|
+
Riddl::Header.new('CPEE-STATUS','giveback'),
|
283
|
+
Riddl::Header.new('CPEE-EVENT','giveback')
|
284
|
+
]
|
285
|
+
else
|
286
|
+
@status = 404
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end #}}}
|
290
|
+
|
291
|
+
class TaskDetails < Riddl::Implementation #{{{
|
292
|
+
def response
|
293
|
+
index = @a[0].activities.index{ |c| c["id"] == @r.last }
|
294
|
+
if index
|
295
|
+
Riddl::Parameter::Complex.new "data","application/json", JSON.generate({:collect => @a[0].activities[index].has_key?('collect') && !@a[0].activities[index]['collect'].nil?, 'url' => @a[0].activities[index]['url'], 'form' => @a[0].activities[index]['form'], 'parameters' => @a[0].activities[index]['parameters'], 'label' => @a[0].activities[index]['label']})
|
296
|
+
else
|
297
|
+
@status = 404
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end #}}}
|
301
|
+
|
302
|
+
class GetOrgModels < Riddl::Implementation #{{{
|
303
|
+
def response
|
304
|
+
out = XML::Smart.string('<orgmodels/>')
|
305
|
+
@a[0].orgmodels.each{|e| out.root.add("orgmodel", e)}
|
306
|
+
Riddl::Parameter::Complex.new "return","text/xml", out.to_s
|
307
|
+
end
|
308
|
+
end #}}}
|
309
|
+
|
310
|
+
class AssignTask < Riddl::Implementation #{{{
|
311
|
+
def response
|
312
|
+
index = @a[0].activities.index{ |c| c["id"] == @r.last }
|
313
|
+
if index
|
314
|
+
user = @p[0].value
|
315
|
+
@a[0].activities[index]["user"] = user if CPEE::Worklist::User::ok?(@a[0].opts,@a[0].activities[index],user)
|
316
|
+
callback_id = @a[0].activities[index]['id']
|
317
|
+
info = CPEE::Worklist::User::info(@a[0].opts,@a[0].activities[index],user)
|
318
|
+
@a[0].activities.serialize
|
319
|
+
@a[0].notify('user/take', :index => callback_id, :user => @p[0].value, :organisation => info)
|
320
|
+
Riddl::Client.new(@a[0].activities[index]['url']).put [
|
321
|
+
Riddl::Header.new('CPEE-UPDATE','true'),
|
322
|
+
Riddl::Header.new('CPEE-STATUS','take'),
|
323
|
+
Riddl::Header.new('CPEE-EVENT','take')
|
324
|
+
]
|
325
|
+
else
|
326
|
+
@status = 404
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end #}}}
|
330
|
+
|
331
|
+
def self::implementation(opts)
|
332
|
+
opts[:ORG_SCHEMA] = ::File.join(__dir__, 'organisation.rng')
|
333
|
+
opts[:topics] = ::File.join(__dir__, 'topics.xml')
|
334
|
+
|
335
|
+
opts[:top] ||= ::File.join(__dir__, 'data')
|
336
|
+
|
337
|
+
opts[:watchdog_frequency] ||= 7
|
338
|
+
opts[:watchdog_start_off] ||= false
|
339
|
+
|
340
|
+
### set redis_cmd to nil if you want to do global
|
341
|
+
### at least redis_path or redis_url and redis_db have to be set if you do global
|
342
|
+
opts[:redis_path] ||= 'redis.sock' # use e.g. /tmp/redis.sock for global stuff. Look it up in your redis config
|
343
|
+
opts[:redis_db] ||= 0
|
344
|
+
### optional redis stuff
|
345
|
+
opts[:redis_url] ||= nil
|
346
|
+
opts[:redis_cmd] ||= 'redis-server --port 0 --unixsocket #redis_path# --unixsocketperm 600 --pidfile #redis_pid# --dir #redis_db_dir# --dbfilename #redis_db_name# --databases 1 --save 900 1 --save 300 10 --save 60 10000 --rdbcompression yes --daemonize yes'
|
347
|
+
opts[:redis_pid] ||= 'redis.pid' # use e.g. /var/run/redis.pid if you do global. Look it up in your redis config
|
348
|
+
opts[:redis_db_name] ||= 'redis.rdb' # use e.g. /var/lib/redis.rdb for global stuff. Look it up in your redis config
|
349
|
+
|
350
|
+
controller = CPEE::Worklist::Controller.new(opts)
|
351
|
+
|
352
|
+
CPEE::redis_connect opts, 'Server Main'
|
353
|
+
|
354
|
+
opts[:sse_keepalive_frequency] ||= 10
|
355
|
+
opts[:sse_connections] = {}
|
356
|
+
|
357
|
+
opts[:finalize_frequency] ||= 10
|
358
|
+
|
359
|
+
CPEE::Message::set_workers(1)
|
360
|
+
|
361
|
+
Proc.new do
|
362
|
+
parallel do
|
363
|
+
CPEE::Worklist::watch_services(opts[:watchdog_start_off],opts[:redis_url],File.join(opts[:basepath],opts[:redis_path]),opts[:redis_db])
|
364
|
+
EM.add_periodic_timer(opts[:watchdog_frequency]) do ### start services
|
365
|
+
CPEE::Worklist::watch_services(opts[:watchdog_start_off],opts[:redis_url],File.join(opts[:basepath],opts[:redis_path]),opts[:redis_db])
|
366
|
+
end
|
367
|
+
EM.defer do ### catch all sse connections
|
368
|
+
CPEE::Notifications::sse_distributor(opts)
|
369
|
+
end
|
370
|
+
EM.add_periodic_timer(opts[:sse_keepalive_frequency]) do
|
371
|
+
CPEE::Notifications::sse_heartbeat(opts)
|
372
|
+
end
|
373
|
+
EM.add_periodic_timer(opts[:finalize_frequency]) do
|
374
|
+
controller.activities.each_with_index do |activity,index|
|
375
|
+
begin
|
376
|
+
if activity['collect'] && activity['collected'] && activity['deadline'] && activity['collected'] >= activity['collect'] && Time.parse(activity['deadline'].to_s) < Time.now
|
377
|
+
activity = controller.activities.delete_at(index)
|
378
|
+
controller.activities.serialize
|
379
|
+
controller.notify('user/finish', :callback_id => activity['id'], :instance_uuid => activity['uuid'], :cpee_callback => activity['url'], :cpee_instance => activity['cpee_instance'], :cpee_base => activity['cpee_base'], :cpee_label => activity['label'], :cpee_activity => activity['cpee_activity_id'], :orgmodel => activity['orgmodel'])
|
380
|
+
Riddl::Client.new(activity['url']).put
|
381
|
+
end
|
382
|
+
rescue => e
|
383
|
+
puts e.message
|
384
|
+
puts e.backtrace
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
cleanup do
|
391
|
+
CPEE::Worklist::cleanup_services(opts[:watchdog_start_off])
|
392
|
+
end
|
393
|
+
|
394
|
+
interface 'main' do
|
395
|
+
run ActivityHappens,controller if post 'activityhappens'
|
396
|
+
run ShowTasks,controller if get
|
397
|
+
on resource 'callbacks' do
|
398
|
+
use CPEE::Callbacks::implementation(opts)
|
399
|
+
end
|
400
|
+
on resource 'orgmodels' do
|
401
|
+
run GetOrgModels,controller if get
|
402
|
+
end
|
403
|
+
on resource 'tasks' do
|
404
|
+
on resource do
|
405
|
+
run AssignTask,controller if put 'uid'
|
406
|
+
run TaskDel,controller if delete
|
407
|
+
end
|
408
|
+
end
|
409
|
+
on resource do
|
410
|
+
run SetStatus, controller, opts[:top] if put 'status'
|
411
|
+
run GetStatus, opts[:top] if get
|
412
|
+
on resource 'tasks' do
|
413
|
+
run ShowUserTasks,controller if get
|
414
|
+
on resource do |r|
|
415
|
+
run TaskDetails,controller if get
|
416
|
+
run TaskTake,controller if put 'take'
|
417
|
+
run TaskGiveBack,controller if put 'giveback'
|
418
|
+
run TaskDel,controller if delete
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
interface 'notifications' do |r|
|
425
|
+
use CPEE::Notifications::implementation('worklist',opts)
|
426
|
+
end
|
427
|
+
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
end
|
432
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<!--
|
2
|
+
This file is part of CPEE-WORKLIST
|
3
|
+
|
4
|
+
CPEE-WORKLIST is free software: you can redistribute it and/or modify it
|
5
|
+
under the terms of the GNU Lesser General Public License as published by the
|
6
|
+
Free Software Foundation, either version 3 of the License, or (at your
|
7
|
+
option) any later version.
|
8
|
+
|
9
|
+
CPEE-WORKLIST is distributed in the hope that it will be useful, but WITHOUT
|
10
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
11
|
+
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
12
|
+
details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU Lesser General Public License
|
15
|
+
along with CPEE-WORKLIST (file LICENSE in the main directory). If not, see
|
16
|
+
<http://www.gnu.org/licenses/>.
|
17
|
+
-->
|
18
|
+
|
19
|
+
<declaration xmlns="http://riddl.org/ns/declaration/1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
|
20
|
+
<interface name="main">
|
21
|
+
<xi:include href="wlengine.xml"/>
|
22
|
+
</interface>
|
23
|
+
<interface name="notifications">
|
24
|
+
<xi:include href="http://www.riddl.org/ns/common-patterns/notifications-producer/2.0/producer.xml"/>
|
25
|
+
</interface>
|
26
|
+
|
27
|
+
<facade>
|
28
|
+
<tile>
|
29
|
+
<layer name="main"/>
|
30
|
+
<layer name="notifications">
|
31
|
+
<apply-to>/</apply-to>
|
32
|
+
</layer>
|
33
|
+
</tile>
|
34
|
+
</facade>
|
35
|
+
</declaration>
|
@@ -0,0 +1,82 @@
|
|
1
|
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" ns="http://cpee.org/ns/organisation/1.0">
|
2
|
+
<start>
|
3
|
+
<element name="organisation">
|
4
|
+
<ref name="units"/>
|
5
|
+
<ref name="roles"/>
|
6
|
+
<ref name="subjects"/>
|
7
|
+
</element>
|
8
|
+
</start>
|
9
|
+
|
10
|
+
<define name="units">
|
11
|
+
<element name='units'>
|
12
|
+
<zeroOrMore>
|
13
|
+
<element name='unit'>
|
14
|
+
<ref name="thing"/>
|
15
|
+
</element>
|
16
|
+
</zeroOrMore>
|
17
|
+
</element>
|
18
|
+
</define>
|
19
|
+
|
20
|
+
<define name="roles">
|
21
|
+
<element name='roles'>
|
22
|
+
<zeroOrMore>
|
23
|
+
<element name='role'>
|
24
|
+
<ref name="thing"/>
|
25
|
+
</element>
|
26
|
+
</zeroOrMore>
|
27
|
+
</element>
|
28
|
+
</define>
|
29
|
+
|
30
|
+
<define name="thing">
|
31
|
+
<attribute name="id">
|
32
|
+
<data type="string"/>
|
33
|
+
</attribute>
|
34
|
+
<zeroOrMore>
|
35
|
+
<element name='parent'>
|
36
|
+
<data type="string"/>
|
37
|
+
</element>
|
38
|
+
</zeroOrMore>
|
39
|
+
<ref name="permissions"/>
|
40
|
+
</define>
|
41
|
+
|
42
|
+
<define name="subjects">
|
43
|
+
<element name='subjects'>
|
44
|
+
<zeroOrMore>
|
45
|
+
<ref name="subject"/>
|
46
|
+
</zeroOrMore>
|
47
|
+
</element>
|
48
|
+
</define>
|
49
|
+
|
50
|
+
<define name="subject">
|
51
|
+
<element name='subject'>
|
52
|
+
<attribute name="id">
|
53
|
+
<data type="string"/>
|
54
|
+
</attribute>
|
55
|
+
<optional>
|
56
|
+
<attribute name="uid">
|
57
|
+
<data type="string"/>
|
58
|
+
</attribute>
|
59
|
+
</optional>
|
60
|
+
<oneOrMore>
|
61
|
+
<element name='relation'>
|
62
|
+
<attribute name="role">
|
63
|
+
<data type="string"/>
|
64
|
+
</attribute>
|
65
|
+
<attribute name="unit">
|
66
|
+
<data type="string"/>
|
67
|
+
</attribute>
|
68
|
+
</element>
|
69
|
+
</oneOrMore>
|
70
|
+
<zeroOrMore>
|
71
|
+
<element><anyName/><data type="string"/></element>
|
72
|
+
</zeroOrMore>
|
73
|
+
</element>
|
74
|
+
</define>
|
75
|
+
|
76
|
+
<define name="permissions">
|
77
|
+
<element name='permissions'>
|
78
|
+
<empty/>
|
79
|
+
</element>
|
80
|
+
</define>
|
81
|
+
|
82
|
+
</grammar>
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# This file is part of CPEE-WORKLIST
|
4
|
+
#
|
5
|
+
# CPEE-WORKLIST is free software: you can redistribute it and/or modify it
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by the
|
7
|
+
# Free Software Foundation, either version 3 of the License, or (at your
|
8
|
+
# option) any later version.
|
9
|
+
#
|
10
|
+
# CPEE-WORKLIST is distributed in the hope that it will be useful, but WITHOUT
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
12
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
13
|
+
# details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
16
|
+
# along with CPEE-WORKLIST (file LICENSE in the main directory). If not, see
|
17
|
+
# <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'json'
|
20
|
+
require 'redis'
|
21
|
+
require 'daemonite'
|
22
|
+
require 'cpee/redis'
|
23
|
+
|
24
|
+
Daemonite.new do |opts|
|
25
|
+
opts[:runtime_opts] += [
|
26
|
+
["--url=URL", "-uURL", "Specify redis url", ->(p){ opts[:redis_url] = p }],
|
27
|
+
["--path=PATH", "-pPATH", "Specify redis path, e.g. /tmp/redis.sock", ->(p){ opts[:redis_path] = p }],
|
28
|
+
["--db=DB", "-dDB", "Specify redis db, e.g. 1", ->(p) { opts[:redis_db] = p.to_i }]
|
29
|
+
]
|
30
|
+
|
31
|
+
on startup do
|
32
|
+
opts[:redis_path] ||= '/tmp/redis.sock'
|
33
|
+
opts[:redis_db] ||= 1
|
34
|
+
|
35
|
+
CPEE::redis_connect opts, 'Server Routing End'
|
36
|
+
opts[:pubsubredis] = opts[:redis_dyn].call 'Server Routing End Sub'
|
37
|
+
end
|
38
|
+
|
39
|
+
run do
|
40
|
+
opts[:pubsubredis].psubscribe('callback-end:*') do |on|
|
41
|
+
on.pmessage do |pat, what, message|
|
42
|
+
_, worker, key = what.split(':',3)
|
43
|
+
index = message.index(' ')
|
44
|
+
instance = message[0...index]
|
45
|
+
opts[:redis].multi do |multi|
|
46
|
+
multi.srem("worklist:#{instance}/callbacks",key)
|
47
|
+
multi.del("worklist:#{instance}/callback/#{key}/uuid")
|
48
|
+
multi.del("worklist:#{instance}/callback/#{key}/label")
|
49
|
+
multi.del("worklist:#{instance}/callback/#{key}/position")
|
50
|
+
multi.del("worklist:#{instance}/callback/#{key}/type")
|
51
|
+
multi.del("worklist:#{instance}/callback/#{key}/subscription")
|
52
|
+
end
|
53
|
+
rescue => e
|
54
|
+
puts e.message
|
55
|
+
puts e.backtrace
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end.go!
|