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