cpee-worklist 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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!