cpee-worklist 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.
- 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!
|