cpee-logging-xes-yaml 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '093abf131db31464c118276deec2c33110a1eada1249b40451fca9eaeee3c38d'
4
- data.tar.gz: 8ba44738e83d6d5487f33026012d9ee813bd4dada97d0e4d4ffeefa0b04494b5
3
+ metadata.gz: b2f390c43e6c32b2abead85f3ead4fa971dda1cb24226c784a7048024c364d53
4
+ data.tar.gz: 4a5b8ffceb7522522568a8499d35118942dfb74a4521df7e45ebec646667cd20
5
5
  SHA512:
6
- metadata.gz: 2b20ebd080a36d9e71ea5e1bf1556d26c2e709fe28e9dd783058111dc4472bd52ecac7562d4e97a1c756a7768e29aec4f7aed4ec9cfeaf1df9e53a40d47e25a8
7
- data.tar.gz: 958888e149af9efc14c16429830a3c54db0e97ac681826f08072a0e902ec036ae9369df8ae5b33599a8968f75fa70f774520e53f2e4429b8aca0923e90f65911
6
+ metadata.gz: 9b472f1e1258dd2c6d88156a088c1e97e79b208bea4b59df1933b5f818443094c19b16c2f6c6f9d4510d785bfb7c9c8bcb1288adcf8f148e3c8ce6d2e10efc06
7
+ data.tar.gz: b5d446c669f4e2e95323dd3c319d97926dca9690746b852855b5ce90b12c91711a6a63e4f4c4c62a9a6db655a68fde18d9e2c28571b57e17de1796b5078df250
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cpee-logging-xes-yaml"
3
- s.version = "1.4.1"
3
+ s.version = "1.5.1"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.license = "LGPL-3.0-or-later"
6
6
  s.summary = "Logging for the cloud process execution engine (cpee.org)"
@@ -0,0 +1,36 @@
1
+ <!--
2
+ This file is part of CPEE-LOGGING-XES-YAML.
3
+
4
+ CPEE-LOGGING-XES-YAML is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Lesser General Public License as published by the Free
6
+ Software Foundation, either version 3 of the License, or (at your option) any
7
+ later version.
8
+
9
+ CPEE-LOGGING-XES-YAML is distributed in the hope that it will be useful, but
10
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
12
+ more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License along with
15
+ CPEE-LOGGING-XES-YAML (file LICENSE in the main directory). If not, see
16
+ <http://www.gnu.org/licenses/>.
17
+ -->
18
+
19
+ <description datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" xmlns="http://riddl.org/ns/description/1.0" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:doc="http://cpee.org/ns/documentation">
20
+
21
+ <message name="yaml">
22
+ <parameter name="log" mimetype="text/yaml"/>
23
+ </message>
24
+ <message name="xml">
25
+ <parameter name="log" mimetype="application/xml"/>
26
+ </message>
27
+
28
+ <resource>
29
+ <resource relative="[a-f0-9-]+.xes.yaml">
30
+ <get out="yaml"/>
31
+ </resource>
32
+ <resource relative="[a-f0-9-]+.xes.xml">
33
+ <get out="xml"/>
34
+ </resource>
35
+ </resource>
36
+ </description>
@@ -0,0 +1,365 @@
1
+ # This file is part of CPEE-LOGGING-XES-YAML.
2
+ #
3
+ # CPEE-LOGGING-XES-YAML 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 Free
5
+ # Software Foundation, either version 3 of the License, or (at your option) any
6
+ # later version.
7
+ #
8
+ # CPEE-LOGGING-XES-YAML is distributed in the hope that it will be useful, but
9
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
11
+ # more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License along with
14
+ # CPEE-LOGGING-XES-YAML (file LICENSE in the main directory). If not, see
15
+ # <http://www.gnu.org/licenses/>.
16
+
17
+ require 'redis'
18
+ require 'json'
19
+ require 'yaml'
20
+ require 'riddl/server'
21
+ require 'time'
22
+
23
+ require_relative 'tools'
24
+
25
+ module CPEE
26
+ module Logging
27
+
28
+ SERVER = File.expand_path(File.join(__dir__,'implementation.xml'))
29
+
30
+ TEMPLATE_XES_XML_START = <<-END
31
+ <log xmlns="http://www.xes-standard.org/" xes.version="2.0" xes.features="nested-attributes">
32
+ <string key="creator" value="cpee.org"/>
33
+ <extension name="Time" prefix="time" uri="http://www.xes-standard.org/time.xesext"/>
34
+ <extension name="Concept" prefix="concept" uri="http://www.xes-standard.org/concept.xesext"/>
35
+ <extension name="ID" prefix="id" uri="http://www.xes-standard.org/identity.xesext"/>
36
+ <extension name="Lifecycle" prefix="lifecycle" uri="http://www.xes-standard.org/lifecycle.xesext"/>
37
+ <extension name="CPEE" prefix="cpee" uri="http://cpee.org/cpee.xesext"/>
38
+ <extension name="stream" prefix="stream" uri="https://cpee.org/datastream/datastream.xesext"/>
39
+ <global scope="trace">
40
+ <string key="concept:name" value="__NOTSPECIFIED__"/>
41
+ <string key="cpee:name" value="__NOTSPECIFIED__"/>
42
+ </global>
43
+ <global scope="event">
44
+ <string key="concept:name" value="__NOTSPECIFIED__"/>
45
+ <string key="concept:instance" value="-1"/>
46
+ <string key="concept:endpoint" value="__NOTSPECIFIED__"/>
47
+ <string key="id:id" value="__NOTSPECIFIED__"/>
48
+ <string key="lifecycle:transition" value="complete" />
49
+ <string key="cpee:lifecycle:transition" value="activity/calling"/>
50
+ <date key="time:timestamp" value="__NOTSPECIFIED__"/>
51
+ </global>
52
+ END
53
+ TEMPLATE_XES_XML_TRC = <<-END
54
+ <trace xmlns="http://www.xes-standard.org/"/>
55
+ END
56
+ TEMPLATE_XES_XML_EVT = <<-END
57
+ <event xmlns="http://www.xes-standard.org/"/>
58
+ END
59
+ TEMPLATE_XES_XML_MID = <<-END
60
+ <trace>
61
+ END
62
+ TEMPLATE_XES_XML_END = <<-END
63
+ </trace>
64
+ </log>
65
+ END
66
+
67
+ class HeaderAndFile #{{{
68
+ def initialize(header, io)
69
+ @header = header
70
+ @io = io
71
+ @position = 0
72
+ end
73
+
74
+ def read(length = nil, outbuf = nil)
75
+ if @position < @header.bytesize
76
+ data = read_header(length)
77
+
78
+ if length && data.bytesize < length
79
+ remaining_length = length - data.bytesize
80
+ file_data = @io.read
81
+ data << file_data if file_data
82
+ end
83
+ else
84
+ data = @io.read(length)
85
+ end
86
+
87
+ return data.nil? || data.empty? && length ? nil : append_to_outbuf(data, outbuf)
88
+ end
89
+
90
+ def rewind
91
+ @io&.rewind
92
+ @position = 0 # returning position is the way rewind does it
93
+ end
94
+
95
+ def close
96
+ @io&.close
97
+ @io = nil
98
+ end
99
+
100
+ def read_header(length)
101
+ chunk = if length
102
+ @header.byteslice(@position, length)
103
+ else
104
+ @header.byteslice(@position..-1)
105
+ end
106
+ @position += chunk.bytesize
107
+ chunk
108
+ end
109
+ private :read_header
110
+
111
+ def append_to_outbuf(data, outbuf)
112
+ outbuf ? outbuf.replace(data || '') : data
113
+ end
114
+ private :append_to_outbuf
115
+ end #}}}
116
+
117
+ class DownloadYAML < Riddl::Implementation
118
+ def response
119
+ opts = @a[0]
120
+ fname = File.join(opts[:log_dir],@r[-1])
121
+ if File.exist?(fname)
122
+ io = File.open fname
123
+ header = File.read(fname.sub(/yaml$/,'header')) if File.exist?(fname.sub(/yaml$/,'header'))
124
+ Riddl::Parameter::Complex::new('log','text/yaml',HeaderAndFile.new(header || '',File.open(fname)))
125
+ else
126
+ @status = 404
127
+ end
128
+ end
129
+ end
130
+
131
+ class DownloadXML < Riddl::Implementation
132
+ def self::rec_type(it) #{{{
133
+ if it.is_a?(String) && it =~ /^[\dT:+.-]+$/ && (Time.parse(it) rescue nil)
134
+ 'x:date'
135
+ elsif it.is_a? Float
136
+ 'x:float'
137
+ elsif it.is_a? Integer
138
+ 'x:int'
139
+ elsif it.is_a? String
140
+ 'x:string'
141
+ end
142
+ end #}}}
143
+
144
+ def self::format_secs(s) #{{{
145
+ return 'long' if s.infinite?
146
+ s = s.to_i
147
+ m = s / 60
148
+ m < 0 ? "#{s}s" : "#{'%02d' % m}m #{'%02d' % (s%60)}s"
149
+ end #}}}
150
+
151
+ def self::rec_a_insert(event,node,level=0) #{{{
152
+ event.each do |i|
153
+ tnode = node
154
+ case i
155
+ when Hash
156
+ tnode = node.add('x:list', 'key' => 'element')
157
+ self::rec_insert(i,tnode,level+1)
158
+ when Array
159
+ tnode = node.add('x:list', 'key' => 'element')
160
+ self::rec_insert(i,tnode,level+1)
161
+ when String
162
+ node.add(rec_type(i), 'key' => 'element', 'value' => (i.empty? ? "__UNSPECIFIED__" : i))
163
+ when Integer, Float
164
+ node.add(rec_type(i), 'key' => 'element', 'value' => i)
165
+ end
166
+ end
167
+ end #}}}
168
+
169
+ def self::rec_insert(event,node,level=0) #{{{
170
+ event.each do |k,v|
171
+ case v
172
+ when String
173
+ node.add(rec_type(v), 'key' => k, 'value' => (v.empty? ? "__UNSPECIFIED__" : v))
174
+ when Integer
175
+ node.add(rec_type(v), 'key' => k, 'value' => v)
176
+ when Float
177
+ node.add(rec_type(v), 'key' => k, 'value' => v)
178
+ when Array
179
+ tnode = node.add('x:list', 'key' => k)
180
+ self::rec_a_insert(v,tnode,level+1)
181
+ when Hash
182
+ tnode = node.add('x:list', 'key' => k)
183
+ self::rec_insert(v,tnode)
184
+ end
185
+ end
186
+ end #}}}
187
+
188
+ def response
189
+ opts = @a[0]
190
+ fname = File.join(opts[:log_dir],@r[-1]).sub(/xml$/,'yaml')
191
+ if File.exist?(fname)
192
+ body = StringIO.new
193
+
194
+ body.write(TEMPLATE_XES_XML_START)
195
+
196
+ io = File.open(fname)
197
+ YAML.load_stream(io) do |e|
198
+ if trace = e.dig('log','trace')
199
+ xml = XML::Smart.string(TEMPLATE_XES_XML_TRC)
200
+ xml.register_namespace 'x', 'http://www.xes-standard.org/'
201
+ trace.each do |t,tv|
202
+ xml.find('//x:trace').each do |ele|
203
+ ele.add('x:string', 'key' => t, 'value' => tv)
204
+ end
205
+ end
206
+ body.write(' ' + xml.root.dump.gsub(/\n/,"\n ") + "\n")
207
+ body.write(TEMPLATE_XES_XML_MID)
208
+ end
209
+ if e.dig('event')
210
+ xml = XML::Smart.string(TEMPLATE_XES_XML_EVT)
211
+ xml.register_namespace 'x', 'http://www.xes-standard.org/'
212
+ DownloadXML::rec_insert(e.dig('event'),xml.root)
213
+ body.write(' ' + xml.root.dump.gsub(/\n/,"\n ") + "\n")
214
+ end
215
+ end
216
+ body.write(TEMPLATE_XES_XML_END)
217
+ body.rewind
218
+
219
+ Riddl::Parameter::Complex::new('log','application/xml',body)
220
+ else
221
+ @status = 404
222
+ end
223
+ end
224
+ end
225
+
226
+ class Handler < Riddl::Implementation
227
+ def response
228
+ opts = @a[0]
229
+ type = @p[0].value
230
+ topic = @p[1].value
231
+ event_name = @p[2].value
232
+ payload = @p[3].value.read
233
+
234
+ ### we write headers into its own file. If race condition at first, no problemo
235
+ unless File.exist? File.join(opts[:log_dir],@h['CPEE_INSTANCE_UUID'] + '.xes.header')
236
+ notification = JSON.parse(payload)
237
+ log = YAML::load(File.read(opts[:template]))
238
+ log['log']['trace']['concept:name'] ||= notification['instance']
239
+ log['log']['trace']['cpee:name'] ||= notification['instance-name'] if notification['instance-name']
240
+ log['log']['trace']['cpee:instance'] ||= notification['instance-uuid']
241
+ log['log']['trace']['cpee:parent_instance'] ||= notification.dig('content','attributes','parent_instance').to_i if notification.dig('content','attributes','parent_instance')
242
+ log['log']['trace']['cpee:parent_instance_uuid'] ||= notification.dig('content','attributes','parent_instance_uuid') if notification.dig('content','attributes','parent_instance_uuid')
243
+ log['log']['trace']['cpee:parent_instance_model'] ||= notification.dig('content','attributes','parent_instance_model') if notification.dig('content','attributes','parent_instance_model')
244
+ log['log']['trace']['cpee:parent_instance_task_id'] ||= notification.dig('content','attributes','parent_instance_task_id') if notification.dig('content','attributes','parent_instance_task_id')
245
+ log['log']['trace']['cpee:parent_instance_task_label'] ||= notification.dig('content','attributes','parent_instance_task_label') if notification.dig('content','attributes','parent_instance_task_label')
246
+ File.open(File.join(opts[:log_dir],@h['CPEE_INSTANCE_UUID']+'.xes.header'),'w'){|f| f.puts log.to_yaml}
247
+ end
248
+
249
+ EM.defer do
250
+ CPEE::Logging::forward opts, topic, event_name, payload
251
+ end if type == 'event'
252
+ EM.defer do
253
+ CPEE::Logging::doc opts, topic, event_name, payload
254
+ end
255
+ nil
256
+ end
257
+ end
258
+
259
+ class Overview < Riddl::Implementation #{{{
260
+ def response
261
+ Riddl::Parameter::Complex.new('overview','text/xml') do
262
+ <<-END
263
+ <overview xmlns='http://riddl.org/ns/common-patterns/notifications-producer/2.0'>
264
+ <topics/>
265
+ <subscriptions/>
266
+ </overview>
267
+ END
268
+ end
269
+ end
270
+ end #}}}
271
+
272
+ class Topics < Riddl::Implementation #{{{
273
+ def response
274
+ opts = @a[0]
275
+ Riddl::Parameter::Complex.new('overview','text/xml') do
276
+ File.read(opts[:topics])
277
+ end
278
+ end
279
+ end #}}}
280
+
281
+ class Subscriptions < Riddl::Implementation #{{{
282
+ def response
283
+ opts = @a[0]
284
+ Riddl::Parameter::Complex.new('subscriptions','text/xml') do
285
+ ret = XML::Smart::string <<-END
286
+ <subscriptions xmlns='http://riddl.org/ns/common-patterns/notifications-producer/2.0'/>
287
+ END
288
+ Dir.glob(File.join(opts[:notifications_dir],'*','subscription.xml')).each do |f|
289
+ ret.root.add('subscription').tap do |n|
290
+ n.attributes['id'] = File.basename(File.dirname(f))
291
+ XML::Smart.open_unprotected(f) do |doc|
292
+ n.attributes['url'] = doc.root.attributes['url']
293
+ end
294
+ end
295
+ end
296
+ ret.to_s
297
+ end
298
+ end
299
+ end #}}}
300
+
301
+ class Subscription < Riddl::Implementation #{{{
302
+ def response
303
+ opts = @a[0]
304
+ id = @r[-1]
305
+ doc = XML::Smart::open_unprotected(File.join(opts[:notifications_dir],id,'subscription.xml'))
306
+ doc.root.attributes['id'] = id
307
+ Riddl::Parameter::Complex.new('subscriptions','text/xml',doc.to_s)
308
+ end
309
+ end #}}}
310
+
311
+ def self::implementation(opts)
312
+ opts[:log_dir] ||= File.expand_path(File.join(__dir__,'logs'))
313
+ opts[:notifications_dir] ||= File.expand_path(File.join(__dir__,'notifications'))
314
+ opts[:template] ||= File.expand_path(File.join(__dir__,'template.xes_yaml'))
315
+ opts[:topics] ||= File.expand_path(File.join(__dir__,'topics.xml'))
316
+ opts[:subscriptions] = {}
317
+
318
+ opts[:sse_keepalive_frequency] ||= 10
319
+
320
+ Dir.glob(File.join(opts[:notifications_dir],'*','subscription.xml')).each do |f|
321
+ XML::Smart::open_unprotected(f) do |doc|
322
+ doc.register_namespace :p, 'http://riddl.org/ns/common-patterns/notifications-producer/2.0'
323
+ doc.find('/p:subscription/p:topic').each do |t|
324
+ t.find('p:event').each do |e|
325
+ opts[:subscriptions][t.attributes['id']+'/'+e.text] ||= []
326
+ opts[:subscriptions][t.attributes['id']+'/'+e.text] << doc.root.attributes['url']
327
+ end
328
+ end
329
+ end
330
+ end
331
+
332
+ Proc.new do
333
+ interface 'access' do
334
+ on resource '[a-f0-9-]+.xes.yaml' do
335
+ run DownloadYAML, opts if get
336
+ end
337
+ on resource '[a-f0-9-]+.xes.xml' do
338
+ run DownloadXML, opts if get
339
+ end
340
+ end
341
+ interface 'events' do
342
+ run Handler, opts if post 'event'
343
+ end
344
+ interface 'notifications' do
345
+ on resource 'notifications' do
346
+ run Overview if get
347
+ on resource 'topics' do
348
+ run Topics, opts if get
349
+ end
350
+ on resource 'subscriptions' do
351
+ run Subscriptions, opts if get
352
+ run CreateSubscription, opts if post 'create_subscription'
353
+ on resource do
354
+ run Subscription, opts if get
355
+ run UpdateSubscription, opts if put 'change_subscription'
356
+ run DeleteSubscription, opts if delete
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ end
365
+ end
@@ -23,9 +23,15 @@
23
23
  <interface name="notifications">
24
24
  <xi:include href="http://www.riddl.org/ns/common-patterns/notifications-producer/2.0/producer.xml"/>
25
25
  </interface>
26
+ <interface name="access">
27
+ <xi:include href="access.xml"/>
28
+ </interface>
26
29
 
27
30
  <facade>
28
31
  <tile>
32
+ <layer name="access">
33
+ <apply-to>/logs</apply-to>
34
+ </layer>
29
35
  <layer name="events">
30
36
  <apply-to>/</apply-to>
31
37
  </layer>
@@ -114,53 +114,6 @@ module CPEE
114
114
  end
115
115
  end
116
116
 
117
- def self::extract_probes(where,xml)
118
- XML::Smart::string(xml) do |doc|
119
- doc.register_namespace 'd', 'http://cpee.org/ns/description/1.0'
120
- doc.find('//d:call | //d:manipulate').each do |c|
121
- File.unlink(where + '_' + c.attributes['id'] + '.probe') rescue nil
122
- c.find('d:annotations/d:_context_data_analysis/d:probes[d:probe]').each do |p|
123
- File.write(where + '_' + c.attributes['id'] + '.probe', p.dump)
124
- end
125
- end
126
- end
127
- end
128
-
129
- def self::extract_annotations(where,xml)
130
- ret = {}
131
- XML::Smart::string(xml) do |doc|
132
- doc.register_namespace 'd', 'http://cpee.org/ns/description/1.0'
133
- doc.find('/d:description | //d:call | //d:manipulate').each do |c|
134
- tid = c.attributes['id'] || 'complex'
135
- fname = where + '_' + tid + '.anno'
136
- nset = if tid == 'complex'
137
- c.find('d:*[starts-with(name(),"_")]')
138
- else
139
- c.find('d:annotations')
140
- end
141
- nset.each do |p|
142
- anno = p.dump
143
- ret[tid] ||= []
144
- ret[tid] << anno
145
- end
146
- if ret[tid]
147
- if ret[tid].length > 1
148
- ret[tid] = "<annotations xmlns=\"http://cpee.org/ns/description/1.0\">\n" +
149
- ret[tid].join("\n") + "\n" +
150
- '</annotations>'
151
- else
152
- ret[tid] = ret[tid][0]
153
- end
154
- hash = Digest::SHA1.hexdigest(ret[tid])
155
- if !File.exist?(fname) || File.read(fname) != hash
156
- File.write(fname,hash)
157
- end
158
- end
159
- end
160
- end
161
- ret
162
- end
163
-
164
117
  def self::extract_result(result)
165
118
  ret = result.map do |res|
166
119
  if res['mimetype'].nil?
@@ -198,30 +151,6 @@ module CPEE
198
151
  e.backtrace[0].gsub(/(\w+):(\d+):in.*/,'Probe ' + pid + ' Line \2: ') + e.message
199
152
  end
200
153
 
201
- def self::persist_values(where,values)
202
- unless File.exist?(where)
203
- File.write(where,'{}')
204
- end
205
- f = File.open(where,'r+')
206
- f.flock(File::LOCK_EX)
207
- json = JSON::load(f) || {}
208
- json.merge!(values)
209
- f.rewind
210
- f.truncate(0)
211
- f.write(JSON.generate(json))
212
- f.flock(File::LOCK_UN)
213
- f.close
214
- end
215
- def self::load_values(where)
216
- ret = nil
217
- File.open(where,'r') do |f|
218
- f.flock(File::LOCK_SH)
219
- ret = JSON::load(f)
220
- f.flock(File::LOCK_UN)
221
- end
222
- ret
223
- end
224
-
225
154
  def self::forward(opts,topic,event_name,payload)
226
155
  if topic == 'state' && event_name == 'change'
227
156
  self::notify(opts,topic,event_name,payload)
@@ -246,29 +175,6 @@ module CPEE
246
175
  parameters = content['parameters']
247
176
  receiving = content['received']
248
177
 
249
- if content['dslx']
250
- CPEE::Logging::extract_probes(File.join(log_dir,instance),content['dslx'])
251
- CPEE::Logging::extract_annotations(File.join(log_dir,instance),content['dslx']).each do |k,v|
252
- so = JSON.parse(notification.to_json)
253
- so['content'].delete('dslx')
254
- so['content'].delete('dsl')
255
- so['content'].delete('description')
256
- so['content']['annotation'] = v
257
- so['content']['activity'] = k
258
- so['topic'] = 'annotation'
259
- so['name'] = 'change'
260
- EM.defer do
261
- self::notify(opts,'annotation','change',so.to_json)
262
- end
263
- end
264
- end
265
-
266
- if topic == 'dataelements' && event_name == 'change'
267
- if content['changed']&.any?
268
- CPEE::Logging::persist_values(File.join(log_dir,instance + '.data.json'),content['values'])
269
- end
270
- end
271
-
272
178
  event = {}
273
179
  event['concept:instance'] = instancenr
274
180
  event['concept:name'] = content['label'] if content['label']
@@ -298,90 +204,79 @@ module CPEE
298
204
  unless parameters['arguments']&.nil?
299
205
  event['data'] = parameters['arguments']
300
206
  end if parameters
301
- if content['changed']&.any?
207
+ if (event_name == 'modify' || event_name == 'change') && content['values']&.any?
302
208
  event['data'] = content['values'].map do |k,v|
303
209
  { 'name' => k, 'value' => v }
304
210
  end
211
+ end
305
212
 
306
- fname = File.join(log_dir,instance + '_' + event['id:id'] + '.probe')
307
- dname = File.join(log_dir,instance + '.data.json')
213
+ if topic == 'task' && event_name == 'probe'
214
+ rs = WEEL::ReadStructure.new(content['data'],{},{},{})
215
+ rc = CPEE::Logging::extract_result(receiving) if receiving && !receiving.empty?
216
+ te = event.dup
308
217
 
309
- # Handle intrinsic data probes
310
- if File.exist?(fname)
311
- rs = WEEL::ReadStructure.new(File.exist?(dname) ? CPEE::Logging::load_values(dname) : {},{},{},{})
312
- XML::Smart::open_unprotected(fname) do |doc|
313
- doc.register_namespace 'd', 'http://cpee.org/ns/description/1.0'
314
- doc.find('//d:probe[d:extractor_type="intrinsic"]').each do |p|
315
- pid = p.find('string(d:id)')
316
- source = p.find('string(d:source)')
317
- val = CPEE::Logging::extract_val(rs,p.find('string(d:extractor_code)'),pid,nil) rescue nil
318
- # Do not add datastream entries if the dataprobes return nil
319
- if val != nil
320
- event['stream:datastream'] ||= []
321
- CPEE::Logging::merge_val(event['stream:datastream'],val,pid,source)
322
- end
218
+ content['probes'].each do |p|
219
+
220
+ if p.dig('probe','extractor_type') == 'intrinsic'
221
+ pid = p.dig('probe','id')
222
+ source = p.dig('probe','source')
223
+ val = CPEE::Logging::extract_val(rs,p.dig('probe','extractor_code'),pid,nil) rescue nil
224
+ # Do not add datastream entries if the dataprobes return nil
225
+ if val != nil
226
+ event['stream:datastream'] ||= []
227
+ CPEE::Logging::merge_val(event['stream:datastream'],val,pid,source)
323
228
  end
324
- end
325
- if event['stream:datastream'] && event['stream:datastream'].any?
326
- EM.defer do
327
- self::notify(
328
- opts,
329
- 'stream',
330
- 'extraction',
331
- notification.merge(
332
- 'topic'=>'stream',
333
- 'name'=>'extraction',
334
- 'datastream'=>event['stream:datastream']
335
- ).to_json
336
- )
229
+ if event['stream:datastream'] && event['stream:datastream'].any?
230
+ EM.defer do
231
+ self::notify(
232
+ opts,
233
+ 'stream',
234
+ 'extraction',
235
+ notification.merge(
236
+ 'topic'=>'stream',
237
+ 'name'=>'extraction',
238
+ 'datastream'=>event['stream:datastream']
239
+ ).to_json
240
+ )
241
+ end
337
242
  end
338
243
  end
339
- end
340
- end
341
- # Handle extrinsic data probes
342
- if topic == 'activity' && event_name == 'receiving' && receiving && !receiving.empty?
343
- fname = File.join(log_dir,instance + '_' + event['id:id'] + '.probe')
344
- dname = File.join(log_dir,instance + '.data.json')
345
244
 
346
- if File.exist?(fname)
347
- te = event.dup
348
-
349
- rs = WEEL::ReadStructure.new(File.exist?(dname) ? CPEE::Logging::load_values(dname) : {},{},{},{})
350
- XML::Smart::open_unprotected(fname) do |doc|
351
- doc.register_namespace 'd', 'http://cpee.org/ns/description/1.0'
352
- if doc.find('//d:probe/d:extractor_type[.="extrinsic"]').any?
353
- rc = CPEE::Logging::extract_result(receiving)
354
- doc.find('//d:probe[d:extractor_type="extrinsic"]').each do |p|
355
- pid = p.find('string(d:id)')
245
+ if receiving && !receiving.empty?
246
+ if p.dig('probe','extractor_type') == 'extrinsic'
247
+ pid = p.dig('probe','id')
248
+ source = p.dig('probe','source')
249
+ val = CPEE::Logging::extract_val(rs,p.dig('probe','extractor_code'),pid,rc) rescue nil
250
+ # Do not add datastream entries if the dataprobes return nil
251
+ if val != nil
356
252
  te['stream:datastream'] ||= []
357
- val = CPEE::Logging::extract_val(rs,p.find('string(d:extractor_code)'),pid,rc) rescue nil
358
- if not val.nil?
359
- # Do not add datastream entries if the dataprobes return nil
360
- CPEE::Logging::merge_val(te['stream:datastream'],val,pid,p.find('string(d:source)'))
253
+ CPEE::Logging::merge_val(te['stream:datastream'],val,pid,source)
254
+ end
255
+ if te['stream:datastream'] && te['stream:datastream'].any?
256
+ te['cpee:lifecycle:transition'] = 'stream/data'
257
+ File.open(File.join(log_dir,instance+'.xes.yaml'),'a') do |f|
258
+ f << {'event' => te}.to_yaml
259
+ end
260
+ EM.defer do
261
+ self::notify(
262
+ opts,
263
+ 'stream',
264
+ 'extraction',
265
+ notification.merge(
266
+ 'topic'=>'stream',
267
+ 'name'=>'extraction',
268
+ 'datastream'=>te['stream:datastream']
269
+ ).to_json
270
+ )
361
271
  end
362
272
  end
363
273
  end
364
274
  end
365
- if te['stream:datastream'] && te['stream:datastream'].any?
366
- te['cpee:lifecycle:transition'] = 'stream/data'
367
- File.open(File.join(log_dir,instance+'.xes.yaml'),'a') do |f|
368
- f << {'event' => te}.to_yaml
369
- end
370
- EM.defer do
371
- self::notify(
372
- opts,
373
- 'stream',
374
- 'extraction',
375
- notification.merge(
376
- 'topic'=>'stream',
377
- 'name'=>'extraction',
378
- 'datastream'=>te['stream:datastream']
379
- ).to_json
380
- )
381
- end
382
- end
275
+
383
276
  end
277
+
384
278
  end
279
+
385
280
  if receiving && !receiving.empty?
386
281
  event['data'] = receiving
387
282
  end
data/server/log CHANGED
@@ -17,7 +17,7 @@
17
17
  # <http://www.gnu.org/licenses/>.
18
18
 
19
19
  require 'rubygems'
20
- require 'cpee-logging-xes-yaml/logging'
20
+ require 'cpee-logging-xes-yaml/implementation'
21
21
 
22
22
  options = {
23
23
  :host => 'localhost',
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpee-logging-xes-yaml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juergen eTM Mangler
@@ -105,8 +105,9 @@ files:
105
105
  - README.md
106
106
  - Rakefile
107
107
  - cpee-logging-xes-yaml.gemspec
108
- - lib/cpee-logging-xes-yaml/logging.rb
109
- - lib/cpee-logging-xes-yaml/logging.xml
108
+ - lib/cpee-logging-xes-yaml/access.xml
109
+ - lib/cpee-logging-xes-yaml/implementation.rb
110
+ - lib/cpee-logging-xes-yaml/implementation.xml
110
111
  - lib/cpee-logging-xes-yaml/template.xes_yaml
111
112
  - lib/cpee-logging-xes-yaml/tools.rb
112
113
  - lib/cpee-logging-xes-yaml/topics.xml
@@ -1,160 +0,0 @@
1
- # This file is part of CPEE-LOGGING-XES-YAML.
2
- #
3
- # CPEE-LOGGING-XES-YAML 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 Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # CPEE-LOGGING-XES-YAML is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
11
- # more details.
12
- #
13
- # You should have received a copy of the GNU Lesser General Public License along with
14
- # CPEE-LOGGING-XES-YAML (file LICENSE in the main directory). If not, see
15
- # <http://www.gnu.org/licenses/>.
16
-
17
- require 'rubygems'
18
- require 'redis'
19
- require 'json'
20
- require 'yaml'
21
- require 'riddl/server'
22
- require 'time'
23
-
24
- require_relative 'tools'
25
-
26
- module CPEE
27
- module Logging
28
-
29
- SERVER = File.expand_path(File.join(__dir__,'logging.xml'))
30
-
31
- class Handler < Riddl::Implementation
32
- def response
33
- opts = @a[0]
34
- type = @p[0].value
35
- topic = @p[1].value
36
- event_name = @p[2].value
37
- payload = @p[3].value.read
38
-
39
- ### we do not write headers for now. Or else we can only do per instance sharding.
40
- # unless File.exist? File.join(opts[:log_dir],@h['CPEE_INSTANCE_UUID'] + '.xes.yaml')
41
- # notification = JSON.parse(payload)
42
- # log = YAML::load(File.read(opts[:template]))
43
- # log['log']['trace']['concept:name'] ||= notification['instance']
44
- # log['log']['trace']['cpee:name'] ||= notification['instance-name'] if notification['instance-name']
45
- # log['log']['trace']['cpee:instance'] ||= notification['instance-uuid']
46
- # log['log']['trace']['cpee:parent_instance'] ||= notification.dig('content','attributes','parent_instance').to_i if notification.dig('content','attributes','parent_instance')
47
- # log['log']['trace']['cpee:parent_instance_uuid'] ||= notification.dig('content','attributes','parent_instance_uuid') if notification.dig('content','attributes','parent_instance_uuid')
48
- # log['log']['trace']['cpee:parent_instance_model'] ||= notification.dig('content','attributes','parent_instance_model') if notification.dig('content','attributes','parent_instance_model')
49
- # log['log']['trace']['cpee:parent_instance_task_id'] ||= notification.dig('content','attributes','parent_instance_task_id') if notification.dig('content','attributes','parent_instance_task_id')
50
- # log['log']['trace']['cpee:parent_instance_task_label'] ||= notification.dig('content','attributes','parent_instance_task_label') if notification.dig('content','attributes','parent_instance_task_label')
51
- # File.open(File.join(opts[:log_dir],@h['CPEE_INSTANCE_UUID']+'.xes.yaml'),'w'){|f| f.puts log.to_yaml}
52
- # end
53
-
54
- EM.defer do
55
- CPEE::Logging::forward opts, topic, event_name, payload
56
- end if type == 'event'
57
- EM.defer do
58
- CPEE::Logging::doc opts, topic, event_name, payload
59
- end
60
- nil
61
- end
62
- end
63
-
64
- class Overview < Riddl::Implementation #{{{
65
- def response
66
- Riddl::Parameter::Complex.new('overview','text/xml') do
67
- <<-END
68
- <overview xmlns='http://riddl.org/ns/common-patterns/notifications-producer/2.0'>
69
- <topics/>
70
- <subscriptions/>
71
- </overview>
72
- END
73
- end
74
- end
75
- end #}}}
76
-
77
- class Topics < Riddl::Implementation #{{{
78
- def response
79
- opts = @a[0]
80
- Riddl::Parameter::Complex.new("overview","text/xml") do
81
- File.read(opts[:topics])
82
- end
83
- end
84
- end #}}}
85
-
86
- class Subscriptions < Riddl::Implementation #{{{
87
- def response
88
- opts = @a[0]
89
- Riddl::Parameter::Complex.new("subscriptions","text/xml") do
90
- ret = XML::Smart::string <<-END
91
- <subscriptions xmlns='http://riddl.org/ns/common-patterns/notifications-producer/2.0'/>
92
- END
93
- Dir.glob(File.join(opts[:notifications_dir],'*','subscription.xml')).each do |f|
94
- ret.root.add('subscription').tap do |n|
95
- n.attributes['id'] = File.basename(File.dirname(f))
96
- XML::Smart.open_unprotected(f) do |doc|
97
- n.attributes['url'] = doc.root.attributes['url']
98
- end
99
- end
100
- end
101
- ret.to_s
102
- end
103
- end
104
- end #}}}
105
-
106
- class Subscription < Riddl::Implementation #{{{
107
- def response
108
- opts = @a[0]
109
- id = @r[-1]
110
- doc = XML::Smart::open_unprotected(File.join(opts[:notifications_dir],id,'subscription.xml'))
111
- doc.root.attributes['id'] = id
112
- Riddl::Parameter::Complex.new("subscriptions","text/xml",doc.to_s)
113
- end
114
- end #}}}
115
-
116
- def self::implementation(opts)
117
- opts[:log_dir] ||= File.expand_path(File.join(__dir__,'logs'))
118
- opts[:notifications_dir] ||= File.expand_path(File.join(__dir__,'notifications'))
119
- opts[:template] ||= File.expand_path(File.join(__dir__,'template.xes_yaml'))
120
- opts[:topics] ||= File.expand_path(File.join(__dir__,'topics.xml'))
121
- opts[:subscriptions] = {}
122
-
123
- Dir.glob(File.join(opts[:notifications_dir],'*','subscription.xml')).each do |f|
124
- XML::Smart::open_unprotected(f) do |doc|
125
- doc.register_namespace :p, 'http://riddl.org/ns/common-patterns/notifications-producer/2.0'
126
- doc.find('/p:subscription/p:topic').each do |t|
127
- t.find('p:event').each do |e|
128
- opts[:subscriptions][t.attributes['id']+'/'+e.text] ||= []
129
- opts[:subscriptions][t.attributes['id']+'/'+e.text] << doc.root.attributes['url']
130
- end
131
- end
132
- end
133
- end
134
-
135
- Proc.new do
136
- interface 'events' do
137
- run Handler, opts if post 'event'
138
- end
139
- interface 'notifications' do
140
- on resource "notifications" do
141
- run Overview if get
142
- on resource "topics" do
143
- run Topics, opts if get
144
- end
145
- on resource "subscriptions" do
146
- run Subscriptions, opts if get
147
- run CreateSubscription, opts if post 'create_subscription'
148
- on resource do
149
- run Subscription, opts if get
150
- run UpdateSubscription, opts if put 'change_subscription'
151
- run DeleteSubscription, opts if delete
152
- end
153
- end
154
- end
155
- end
156
- end
157
- end
158
-
159
- end
160
- end