ruby-xes 0.1.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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +41 -0
- data/Rakefile +16 -0
- data/lib/xes.rb +18 -0
- data/lib/xes/attribute-accessor.rb +121 -0
- data/lib/xes/attribute.rb +201 -0
- data/lib/xes/classifier.rb +63 -0
- data/lib/xes/document.rb +48 -0
- data/lib/xes/event.rb +50 -0
- data/lib/xes/extension.rb +73 -0
- data/lib/xes/format-error.rb +15 -0
- data/lib/xes/global.rb +75 -0
- data/lib/xes/log.rb +158 -0
- data/lib/xes/trace.rb +57 -0
- data/lib/xes/version.rb +4 -0
- data/ruby-xes.gemspec +24 -0
- data/test/spec_attribute.rb +150 -0
- data/test/spec_classifier.rb +26 -0
- data/test/spec_document.rb +19 -0
- data/test/spec_event.rb +102 -0
- data/test/spec_extension.rb +34 -0
- data/test/spec_global.rb +131 -0
- data/test/spec_log.rb +90 -0
- data/test/spec_trace.rb +113 -0
- metadata +128 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
module XES
|
2
|
+
# Classifier represents "classifier" element of XES.
|
3
|
+
class Classifier
|
4
|
+
# @return [String]
|
5
|
+
# classifier name
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# @return [String]
|
9
|
+
# classifier keys
|
10
|
+
attr_reader :keys
|
11
|
+
|
12
|
+
# Create a XES classifier element.
|
13
|
+
#
|
14
|
+
# @param name [String]
|
15
|
+
# classifier name
|
16
|
+
# @param keys [String]
|
17
|
+
# classifier keys
|
18
|
+
def initialize(name, keys)
|
19
|
+
@name = name
|
20
|
+
@keys = keys
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return true if the element is formattable.
|
24
|
+
#
|
25
|
+
# @return [Boolean]
|
26
|
+
# true if the element is formattable
|
27
|
+
def formattable?
|
28
|
+
not(@name.nil? or @keys.nil?)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Format as a XML element.
|
32
|
+
#
|
33
|
+
# @return [REXML::Element]
|
34
|
+
# XML element
|
35
|
+
def format
|
36
|
+
raise FormatError.new(self) unless formattable?
|
37
|
+
|
38
|
+
REXML::Element.new("classifier").tap do |ext|
|
39
|
+
ext.attributes["name"] = @name
|
40
|
+
ext.attributes["keys"] = @keys
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @api private
|
45
|
+
def ==(other)
|
46
|
+
return false unless other.kind_of?(self.class)
|
47
|
+
@name == other.name and @keys == other.keys
|
48
|
+
end
|
49
|
+
alias :eql? :"=="
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
def hash
|
53
|
+
@name.hash + @keys.hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# known XES classifier elements
|
58
|
+
CLASSIFIER = {
|
59
|
+
:mxml_legacy_classifier => Classifier.new("MXML Legacy Classifier", "concept:name lifecycle:transition"),
|
60
|
+
:event_name => Classifier.new("Event Name", "concept:name"),
|
61
|
+
:resource => Classifier.new("Resource", "org:resource")
|
62
|
+
}
|
63
|
+
end
|
data/lib/xes/document.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module XES
|
2
|
+
# Document is a class for XES event log doucment.
|
3
|
+
class Document
|
4
|
+
# @return [Log]
|
5
|
+
# log element
|
6
|
+
attr_accessor :log
|
7
|
+
|
8
|
+
# @param log [Log]
|
9
|
+
def initialize(log=nil)
|
10
|
+
@log = log
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return true if the document is formattable.
|
14
|
+
#
|
15
|
+
# @return [Boolean]
|
16
|
+
# true if the element is formattable
|
17
|
+
def formattable?
|
18
|
+
not(@log.nil?) and @log.formattable?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Format as a XML document.
|
22
|
+
#
|
23
|
+
# @return [REXML::Document]
|
24
|
+
# XML document
|
25
|
+
# @raise FormatError
|
26
|
+
# format error when the document is not formattable
|
27
|
+
def format
|
28
|
+
raise FormatError.new(self) unless formattable?
|
29
|
+
|
30
|
+
REXML::Document.new.tap do |doc|
|
31
|
+
doc << REXML::XMLDecl.new
|
32
|
+
doc.elements << @log.format
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
def ==(other)
|
38
|
+
return false unless other.kind_of?(self.class)
|
39
|
+
@log == other.log
|
40
|
+
end
|
41
|
+
alias :eql? :"=="
|
42
|
+
|
43
|
+
# @api private
|
44
|
+
def hash
|
45
|
+
@attributes.hash + @events.hash
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/xes/event.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module XES
|
2
|
+
# XESEvent represents "event" element of XES.
|
3
|
+
class Event
|
4
|
+
include EventAttributeAccessor
|
5
|
+
|
6
|
+
# @return [Array<Attribute>]
|
7
|
+
# event attributes
|
8
|
+
attr_accessor :attributes
|
9
|
+
|
10
|
+
# Create a XES event.
|
11
|
+
#
|
12
|
+
# @param attributes [Array<Attribute>]
|
13
|
+
# attributes of the event
|
14
|
+
def initialize(attributes=[])
|
15
|
+
@attributes = attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return true if the element is formattable.
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
21
|
+
# true if the element is formattable
|
22
|
+
def formattable?
|
23
|
+
not(@attributes.empty?)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Format as a XML element.
|
27
|
+
#
|
28
|
+
# @return [REXML::Element]
|
29
|
+
# XML element
|
30
|
+
def format
|
31
|
+
REXML::Element.new("event").tap do |event|
|
32
|
+
@attributes.each do |attribute|
|
33
|
+
event.elements << attribute.format if attribute.formattable?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @api private
|
39
|
+
def ==(other)
|
40
|
+
return false unless other.kind_of?(self.class)
|
41
|
+
@attributes == other.attributes
|
42
|
+
end
|
43
|
+
alias :eql? :"=="
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
def hash
|
47
|
+
@attributes.hash
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module XES
|
2
|
+
# Extension represents "extension" element of XES.
|
3
|
+
class Extension
|
4
|
+
# @return [String]
|
5
|
+
# extension name
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# @return [String]
|
9
|
+
# extension prefix
|
10
|
+
attr_reader :prefix
|
11
|
+
|
12
|
+
# @return [String]
|
13
|
+
# extension URI
|
14
|
+
attr_reader :uri
|
15
|
+
|
16
|
+
# Create a XES extension element.
|
17
|
+
#
|
18
|
+
# @param name [String]
|
19
|
+
# extension name
|
20
|
+
# @param prefix [String]
|
21
|
+
# extension prefix
|
22
|
+
# @param uri [String]
|
23
|
+
# extension definition URI
|
24
|
+
def initialize(name, prefix, uri)
|
25
|
+
@name = name
|
26
|
+
@prefix = prefix
|
27
|
+
@uri = uri
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return true if the element is formattable.
|
31
|
+
#
|
32
|
+
# @return [Boolean]
|
33
|
+
# true if the element is formattable
|
34
|
+
def formattable?
|
35
|
+
not(@name.nil? or @prefix.nil? or @uri.nil?)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Format as a XML element.
|
39
|
+
#
|
40
|
+
# @return [REXML::Element]
|
41
|
+
# XML element
|
42
|
+
def format
|
43
|
+
raise FormatError.new(self) unless formattable?
|
44
|
+
|
45
|
+
REXML::Element.new("extension").tap do |ext|
|
46
|
+
ext.attributes["name"] = @name
|
47
|
+
ext.attributes["prefix"] = @prefix
|
48
|
+
ext.attributes["uri"] = @uri
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @api private
|
53
|
+
def ==(other)
|
54
|
+
return false unless other.kind_of?(self.class)
|
55
|
+
@name == other.name and @prefix == other.prefix and @uri == other.uri
|
56
|
+
end
|
57
|
+
alias :eql? :"=="
|
58
|
+
|
59
|
+
# @api private
|
60
|
+
def hash
|
61
|
+
@name.hash + @prefix.hash + @uri.hash
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# known XES extension elements
|
66
|
+
EXTENSION = {
|
67
|
+
:concept => Extension.new("Concept", "concept", "http://www.xes-standard.org/concept.xesext"),
|
68
|
+
:identity => Extension.new("Identity", "identity", "http://www.xes-standard.org/identity.xesext"),
|
69
|
+
:time => Extension.new("Time", "time", "http://www.xes-standard.org/time.xesext"),
|
70
|
+
:lifecycle => Extension.new("Lifecycle", "lifecycle", "http://www.xes-standard.org/lifecycle.xesext"),
|
71
|
+
:organizational => Extension.new("Organizational", "org", "http://www.xes-standard.org/org.xesext")
|
72
|
+
}
|
73
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module XES
|
2
|
+
# FormatError is raised when you failed to format XES element.
|
3
|
+
class FormatError < StandardError
|
4
|
+
# @param element [Object]
|
5
|
+
# object that caused to raise this exception
|
6
|
+
def initialize(element)
|
7
|
+
@element = element
|
8
|
+
end
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
def message
|
12
|
+
"Error happened on formatting %s" % @element.inspect
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/xes/global.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
module XES
|
2
|
+
# Global represents "global" element of XES.
|
3
|
+
class Global
|
4
|
+
class << self
|
5
|
+
# Create an event global. The object extended by EventAttributeAccessor.
|
6
|
+
#
|
7
|
+
# @param attributes [Array<Attribute>]
|
8
|
+
# event global attributes
|
9
|
+
def event(attributes=[])
|
10
|
+
new("event", attributes).tap do |global|
|
11
|
+
global.extend EventAttributeAccessor
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a trace global. The object extended by TraceAttributeAccessor.
|
16
|
+
#
|
17
|
+
# @param attributes [Array<Attribute>]
|
18
|
+
# trace global attributes
|
19
|
+
def trace(attributes=[])
|
20
|
+
new("trace", attributes).tap do |global|
|
21
|
+
global.extend TraceAttributeAccessor
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
# scope name
|
28
|
+
attr_reader :scope
|
29
|
+
|
30
|
+
# @return [Array<Attribute>]
|
31
|
+
# global attributes
|
32
|
+
attr_accessor :attributes
|
33
|
+
|
34
|
+
# @param scope [String]
|
35
|
+
# scope of global attributes
|
36
|
+
# @param attributes [Arrray<Attribute>]
|
37
|
+
# global attributes
|
38
|
+
def initialize(scope, attributes=[])
|
39
|
+
@scope = scope
|
40
|
+
@attributes = attributes
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return true if the element is formattable.
|
44
|
+
#
|
45
|
+
# @return [Boolean]
|
46
|
+
# true if the element is formattable
|
47
|
+
def formattable?
|
48
|
+
not(@attributes.empty?)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Format as a XML element.
|
52
|
+
#
|
53
|
+
# @return [REXML::Element]
|
54
|
+
# XML element
|
55
|
+
def format
|
56
|
+
raise FormatError.new(self) unless formattable?
|
57
|
+
|
58
|
+
REXML::Element.new("global").tap do |global|
|
59
|
+
global.attributes["scope"] = @scope.to_s
|
60
|
+
@attributes.each {|attribute| global.elements << attribute.format}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @api private
|
65
|
+
def ==(other)
|
66
|
+
@scope == other.scope and @attributes == other.attributes
|
67
|
+
end
|
68
|
+
alias :eql? :"=="
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
def hash
|
72
|
+
@scope.hash + @attributes.hash
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/xes/log.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
module XES
|
2
|
+
# Log represents log element of XES.
|
3
|
+
class Log
|
4
|
+
extend AttributeAccessor
|
5
|
+
|
6
|
+
# @!attribute [rw] concept_name
|
7
|
+
# @return [String]
|
8
|
+
# the value of attribute "concept:name"
|
9
|
+
define_attribute "concept:name", "string"
|
10
|
+
|
11
|
+
# @!attribute [rw] lifecycle_model
|
12
|
+
# @return [String]
|
13
|
+
# the value of attribute "lifecycle:model"
|
14
|
+
define_attribute "lifecycle:model", "string"
|
15
|
+
|
16
|
+
# @!attribute [rw] semantic_modelReference
|
17
|
+
# @return [String]
|
18
|
+
# the value of attribute "semantic:modelReference"
|
19
|
+
define_attribute "semantic:modelReference", "string"
|
20
|
+
|
21
|
+
# @!attribute [rw] identity_id
|
22
|
+
# @return [String]
|
23
|
+
# the value of attribute "identity:id"
|
24
|
+
define_attribute "identity:id", "id"
|
25
|
+
|
26
|
+
class << self
|
27
|
+
# Create new instance with default values.
|
28
|
+
#
|
29
|
+
# @return [Log]
|
30
|
+
# new log instance
|
31
|
+
def default
|
32
|
+
new.tap do |log|
|
33
|
+
log.xes_version = "1.4"
|
34
|
+
log.xes_features = "nested-attributes"
|
35
|
+
log.openxes_version = "1.0RC7"
|
36
|
+
log.extensions = [
|
37
|
+
EXTENSION[:concept],
|
38
|
+
EXTENSION[:identity],
|
39
|
+
EXTENSION[:time],
|
40
|
+
EXTENSION[:lifecycle],
|
41
|
+
EXTENSION[:organizational],
|
42
|
+
]
|
43
|
+
log.classifiers = [
|
44
|
+
CLASSIFIER[:mxml_legacy_classifier],
|
45
|
+
CLASSIFIER[:event_name],
|
46
|
+
CLASSIFIER[:resource]
|
47
|
+
]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String]
|
53
|
+
# XES version
|
54
|
+
attr_accessor :xes_version
|
55
|
+
|
56
|
+
# @return [String]
|
57
|
+
# XES features
|
58
|
+
attr_accessor :xes_features
|
59
|
+
|
60
|
+
# @return [String]
|
61
|
+
# openxes version for faking ProM
|
62
|
+
attr_accessor :openxes_version
|
63
|
+
|
64
|
+
# @return [String]
|
65
|
+
# xmlns value
|
66
|
+
attr_accessor :xmlns
|
67
|
+
|
68
|
+
# @return [Array<Extension>]
|
69
|
+
# XES extensions
|
70
|
+
attr_accessor :extensions
|
71
|
+
|
72
|
+
# @return [Array<Classifier>]
|
73
|
+
# XES classifiers
|
74
|
+
attr_accessor :classifiers
|
75
|
+
|
76
|
+
# @return [Array<Global>]
|
77
|
+
# XES global elements for event attributes
|
78
|
+
attr_accessor :event_global
|
79
|
+
|
80
|
+
# @return [Array<Global>]
|
81
|
+
# XES global elements for trace attributes
|
82
|
+
attr_accessor :trace_global
|
83
|
+
|
84
|
+
# @return [Array<Attribute>]
|
85
|
+
# XES attribute elements
|
86
|
+
attr_accessor :attributes
|
87
|
+
|
88
|
+
# @return [Array<XESTrace>]
|
89
|
+
# XES trace elements
|
90
|
+
attr_accessor :traces
|
91
|
+
|
92
|
+
def initialize
|
93
|
+
@xes_version = "1.4"
|
94
|
+
@xes_features = ""
|
95
|
+
@openxes_version = nil
|
96
|
+
@xmlns = "http://www.xes-standard.org/"
|
97
|
+
@extensions = []
|
98
|
+
@event_global = Global.event
|
99
|
+
@trace_global = Global.trace
|
100
|
+
@classifiers = []
|
101
|
+
@attributes = []
|
102
|
+
@traces = []
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return true if the element is formattable.
|
106
|
+
#
|
107
|
+
# @return [Boolean]
|
108
|
+
# true if the element is formattable
|
109
|
+
def formattable?
|
110
|
+
@traces.any? {|trace| trace.formattable?}
|
111
|
+
end
|
112
|
+
|
113
|
+
# Format as a XML element.
|
114
|
+
#
|
115
|
+
# @return [REXML::Element]
|
116
|
+
# XML element
|
117
|
+
# @raise FormatError
|
118
|
+
# format error when the log is formattable
|
119
|
+
def format
|
120
|
+
raise FormatError.new(self) unless formattable?
|
121
|
+
|
122
|
+
REXML::Element.new("log").tap do |log|
|
123
|
+
log.attributes["xes.version"] = @xes_version.to_s if @xes_version
|
124
|
+
log.attributes["xes.features"] = @xes_features.to_s if @xes_features
|
125
|
+
log.attributes["openxes.version"] = @openxes_version.to_s if @openxes_version
|
126
|
+
log.attributes["xmlns"] = @xmlns.to_s if @xmlns
|
127
|
+
@extensions.each {|ext| log.elements << ext.format if ext.formattable?}
|
128
|
+
@classifiers.each {|classifier| log.elements << classifier.format if classifier.formattable?}
|
129
|
+
log.elements << @event_global.format if @event_global.formattable?
|
130
|
+
log.elements << @trace_global.format if @trace_global.formattable?
|
131
|
+
@attributes.each {|attribute| log.elements << attribute.format if attribute.formattable?}
|
132
|
+
@traces.each {|trace| log.elements << trace.format if trace.formattable?}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# @api private
|
137
|
+
def ==(other)
|
138
|
+
return false unless other.kind_of?(self.class)
|
139
|
+
return false unless @xes_version == other.xes_version
|
140
|
+
return false unless @xes_features == other.xes_features
|
141
|
+
return false unless @openxes_version == other.openxes_version
|
142
|
+
return false unless @xmlns == other.xmlns
|
143
|
+
return false unless @extensions == other.extensions
|
144
|
+
return false unless @event_global == other.event_global
|
145
|
+
return false unless @trace_global == other.trace_global
|
146
|
+
return false unless @classifiers == other.classifiers
|
147
|
+
return false unless @attributes == other.attributes
|
148
|
+
return false unless @traces == other.traces
|
149
|
+
return true
|
150
|
+
end
|
151
|
+
alias :eql? :"=="
|
152
|
+
|
153
|
+
# @api private
|
154
|
+
def hash
|
155
|
+
@attributes.hash + @events.hash
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|