wikidata-diff-analyzer 0.1.1 → 2.0.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.
@@ -1,5 +1,8 @@
1
+ require_relative 'reference_analyzer'
2
+ require_relative 'qualifier_analyzer'
3
+
1
4
  class ClaimAnalyzer
2
- def self.isolate_claim_differences(current_content, parent_content)
5
+ def self.isolate_claims_differences(current_content, parent_content)
3
6
  # Initialize empty arrays to store the added, removed, and changed claims
4
7
  added_claims = []
5
8
  removed_claims = []
@@ -11,234 +14,114 @@ class ClaimAnalyzer
11
14
  removed_qualifiers = []
12
15
  changed_qualifiers = []
13
16
 
14
- if !current_content["claims"].is_a?(Hash) || !parent_content["claims"].is_a?(Hash)
15
- return {
16
- added_claims: added_claims,
17
- removed_claims: removed_claims,
18
- changed_claims: changed_claims,
19
- added_references: added_references,
20
- removed_references: removed_references,
21
- changed_references: changed_references,
22
- added_qualifiers: added_qualifiers,
23
- removed_qualifiers: removed_qualifiers,
24
- changed_qualifiers: changed_qualifiers
25
- }
26
- end
27
- # Iterate over each claim key in the current content
28
- current_content["claims"].each do |claim_key, current_claims|
29
- # Check if the claim key exists in the parent content
30
- if parent_content["claims"].key?(claim_key)
31
- parent_claims = parent_content["claims"][claim_key]
32
- # Iterate over each claim in the current and parent content
33
- current_claims.each_with_index do |current_claim, index|
34
- parent_claim = parent_claims[index]
35
- if parent_claim.nil?
36
- # Claim was added
37
- added_claims << { key: claim_key, index: index }
38
- # check if there's any references or qualifiers in this claim
39
- added_references = reference_updates(current_claim, added_references, claim_key, index)
40
- added_qualifiers = qualifier_updates(current_claim, added_qualifiers, claim_key, index)
41
-
42
- elsif current_claim != parent_claim
43
- # Claim was changed
44
- changed_claims << { key: claim_key, index: index }
45
- # check if there's any references or qualifiers in this claim
46
- changed = handle_changed_references(current_claim, parent_claim, changed_references, added_references, removed_references, claim_key, index)
47
- added_references = changed[:added_references]
48
- removed_references = changed[:removed_references]
49
- changed_references = changed[:changed_references]
50
- changed_qualifiers = handle_changed_qualifiers(current_claim, parent_claim, changed_qualifiers, added_qualifiers, removed_qualifiers, claim_key, index)
51
- added_qualifiers = changed_qualifiers[:added_qualifiers]
52
- removed_qualifiers = changed_qualifiers[:removed_qualifiers]
53
- changed_qualifiers = changed_qualifiers[:changed_qualifiers]
54
- end
55
- end
56
- # Check for removed claims
57
- parent_claims.each_with_index do |parent_claim, index|
58
- current_claim = current_claims[index]
59
- if current_claim.nil?
60
- # Claim was removed
61
- removed_claims << { key: claim_key, index: index }
62
-
63
- # check if there's any references or qualifiers in this claim
64
- removed_references = reference_updates(parent_claim, removed_references, claim_key, index)
65
- removed_qualifiers = qualifier_updates(parent_claim, removed_qualifiers, claim_key, index)
66
- end
67
- end
17
+ if current_content.nil?
18
+ current_content_claims = {}
68
19
  else
69
- # All claims in current content with this key were added
70
- current_claims.each_index do |index|
71
- added_claims << { key: claim_key, index: index }
72
- # check if there's any references or qualifiers in this claim
73
- added_references = reference_updates(current_claims[index], added_references, claim_key, index)
74
- added_qualifiers = qualifier_updates(current_claims[index], added_qualifiers, claim_key, index)
20
+ current_content_claims = current_content["claims"]
21
+ if !current_content_claims.is_a?(Hash)
22
+ current_content_claims = {}
75
23
  end
76
24
  end
77
- end
25
+
78
26
 
79
- parent_content["claims"].each do |claim_key, parent_claims|
80
- # current content[claims] can be nil
81
- parent_claims.each_index do |index|
82
- if current_content["claims"].nil? || !current_content["claims"].key?(claim_key)
83
- removed_claims << { key: claim_key, index: index }
84
- # check if there's any references or qualifiers in this claim
85
- removed_references = reference_updates(parent_claims[index], removed_references, claim_key, index)
86
- removed_qualifiers = qualifier_updates(parent_claims[index], removed_qualifiers, claim_key, index)
27
+ if parent_content.nil?
28
+ parent_content_claims = {}
29
+ else
30
+ parent_content_claims = parent_content["claims"]
31
+ if !parent_content_claims.is_a?(Hash)
32
+ parent_content_claims = {}
87
33
  end
88
34
  end
89
- end
90
-
91
- # puts "Added claims: #{added_claims}"
92
- # puts "Removed claims: #{removed_claims}"
93
- # puts "Changed claims: #{changed_claims}"
94
- # puts "Added references: #{added_references}"
95
- # puts "Removed references: #{removed_references}"
96
- # puts "Changed references: #{changed_references}"
97
- # puts "Added qualifiers: #{added_qualifiers}"
98
- # puts "Removed qualifiers: #{removed_qualifiers}"
99
- # puts "Changed qualifiers: #{changed_qualifiers}"
100
-
101
-
102
- {
103
- added_claims: added_claims,
104
- removed_claims: removed_claims,
105
- changed_claims: changed_claims,
106
- added_references: added_references,
107
- removed_references: removed_references,
108
- changed_references: changed_references,
109
- added_qualifiers: added_qualifiers,
110
- removed_qualifiers: removed_qualifiers,
111
- changed_qualifiers: changed_qualifiers
112
- }
113
- end
114
-
115
- # helper method for adding and removing references
116
- def self.reference_updates(claim, updated_references, claim_key, claim_index)
117
- if claim["references"]
118
- claim["references"].each_with_index do |current_ref, ref_index|
119
- updated_references << { claim_key: claim_key, claim_index: claim_index, reference_index: ref_index }
120
- end
121
- end
122
- updated_references
123
- end
124
-
125
- # helper method for changed references
126
- def self.handle_changed_references(current_claim, parent_claim, changed_references, added_references, removed_references, claim_key, claim_index)
127
- current_references = current_claim["references"] ? current_claim["references"] : []
128
- parent_references = parent_claim["references"] ? parent_claim["references"] : []
129
-
130
- current_references.each_with_index do |current_ref, ref_index|
131
- if parent_references.empty?
132
- added_references << { claim_key: claim_key, claim_index: claim_index, reference_index: ref_index }
133
- elsif !parent_references.include?(current_ref)
134
- added_references << { claim_key: claim_key, claim_index: claim_index, reference_index: ref_index }
135
- elsif ref_modified?(current_ref, parent_references)
136
- changed_references << { claim_key: claim_key, claim_index: claim_index, reference_index: ref_index }
137
- end
138
- end
139
-
140
- parent_references.each_with_index do |parent_ref, ref_index|
141
- if !current_references.include?(parent_ref)
142
- removed_references << { claim_key: claim_key, claim_index: claim_index, reference_index: ref_index }
143
- end
144
- end
35
+
36
+ # if parentid is 0, add all current claims as added claims and return it
37
+ if parent_content.nil?
38
+ current_content_claims.each do |claim_key, current_claims|
39
+ current_claims.each_with_index do |current_claim, index|
40
+ added_claims << { key: claim_key, index: index }
41
+ # check if there's any references or qualifiers in this claim
42
+ added_references = ReferenceAnalyzer.reference_updates(current_claim, added_references, claim_key, index)
43
+ added_qualifiers = QualifierAnalyzer.qualifier_updates(current_claim, added_qualifiers, claim_key, index)
44
+ end
45
+ end
46
+ else
47
+ # Iterate over each claim key in the current content
48
+ current_content_claims.each do |claim_key, current_claims|
49
+ # Check if the claim key exists in the parent content
50
+ if parent_content_claims.key?(claim_key)
51
+ parent_claims = parent_content_claims[claim_key]
52
+ # Iterate over each claim in the current and parent content
53
+ current_claims.each_with_index do |current_claim, index|
54
+ parent_claim = parent_claims[index]
55
+ if parent_claim.nil?
56
+ # Claim was added
57
+ added_claims << { key: claim_key, index: index }
58
+ # check if there's any references or qualifiers in this claim
59
+ added_references = ReferenceAnalyzer.reference_updates(current_claim, added_references, claim_key, index)
60
+ added_qualifiers = QualifierAnalyzer.qualifier_updates(current_claim, added_qualifiers, claim_key, index)
145
61
 
146
- {
147
- added_references: added_references,
148
- removed_references: removed_references,
149
- changed_references: changed_references
150
- }
151
- end
62
+ elsif current_claim != parent_claim
63
+ # Claim was changed
64
+ changed_claims << { key: claim_key, index: index }
65
+ # check if there's any references or qualifiers in this claim
66
+ changed_references_hash = ReferenceAnalyzer.handle_changed_references(current_claim, parent_claim, changed_references, added_references, removed_references, claim_key, index)
152
67
 
153
- # helper method for checking if a reference has been modified
154
- def self.ref_modified?(current_reference, parent_references)
155
- parent_references.each do |parent_reference|
156
- if current_reference["snaks"] != parent_reference["snaks"]
157
- return true
158
- end
159
- end
160
- false
161
- end
68
+ added_references = changed_references_hash[:added_references]
69
+ removed_references = changed_references_hash[:removed_references]
70
+ changed_references = changed_references_hash[:changed_references]
162
71
 
163
- # helper method for adding qualifiers
164
- # handles added and removed qualifiers
165
- def self.qualifier_updates(claim, updated_qualifiers, claim_key, claim_index)
166
- if claim["qualifiers"]
167
- qualifiers = claim["qualifiers"]
168
- qualifiers.each do |qualifier_key, qualifier_values|
169
- qualifier_values.each_with_index do |qualifier_value, qualifier_index|
170
- updated_qualifiers << {
171
- claim_key: claim_key,
172
- claim_index: claim_index,
173
- qualifier_key: qualifier_key,
174
- qualifier_index: qualifier_index
175
- }
176
- end
177
- end
178
- end
179
- updated_qualifiers
180
- end
72
+ changed_qualifiers_hash = QualifierAnalyzer.handle_changed_qualifiers(current_claim, parent_claim, changed_qualifiers, added_qualifiers, removed_qualifiers, claim_key, index)
181
73
 
182
- # helper method for changed qualifiers
183
- def self.handle_changed_qualifiers(current_claim, parent_claim, changed_qualifiers, added_qualifiers, removed_qualifiers, claim_key, claim_index)
184
- current_qualifiers = current_claim["qualifiers"] ? current_claim["qualifiers"] : {}
185
- parent_qualifiers = parent_claim["qualifiers"] ? parent_claim["qualifiers"] : {}
74
+ added_qualifiers = changed_qualifiers_hash[:added_qualifiers]
75
+ removed_qualifiers = changed_qualifiers_hash[:removed_qualifiers]
76
+ changed_qualifiers = changed_qualifiers_hash[:changed_qualifiers]
77
+ end
78
+ end
79
+ # Check for removed claims
80
+ parent_claims.each_with_index do |parent_claim, index|
81
+ current_claim = current_claims[index]
82
+ if current_claim.nil?
83
+ # Claim was removed
84
+ removed_claims << { key: claim_key, index: index }
186
85
 
187
- current_qualifiers.each do |qualifier_key, qualifier_values|
188
- qualifier_values.each_with_index do |qualifier_value, qualifier_index|
189
- if parent_qualifiers.key?(qualifier_key)
190
- parent = parent_qualifiers[qualifier_key]
191
- end
192
- # Check if the qualifier index exists in the parent content
193
- if !parent.nil?
194
- parent = parent[qualifier_index]
195
- # check if the parent claim was changed by comparing the objects first
196
- if parent != qualifier_value
197
- # Claim was changed
198
- changed_qualifiers << {
199
- claim_key: claim_key,
200
- claim_index: claim_index,
201
- qualifier_key: qualifier_key,
202
- qualifier_index: qualifier_index
203
- }
204
- end
86
+ # check if there's any references or qualifiers in this claim
87
+ removed_references = ReferenceAnalyzer.reference_updates(parent_claim, removed_references, claim_key, index)
88
+ removed_qualifiers = QualifierAnalyzer.qualifier_updates(parent_claim, removed_qualifiers, claim_key, index)
89
+ end
90
+ end
205
91
  else
206
- # Claim was added
207
- added_qualifiers << {
208
- claim_key: claim_key,
209
- claim_index: claim_index,
210
- qualifier_key: qualifier_key,
211
- qualifier_index: qualifier_index
212
- }
213
- end
214
- end
215
- end
216
- # Check for removed claims
217
- parent_qualifiers.each do |qualifier_key, qualifier_values|
218
- qualifier_values.each_with_index do |qualifier_value, qualifier_index|
219
- if current_qualifiers.key?(qualifier_key)
220
- current = current_qualifiers[qualifier_key]
92
+ # All claims in current content with this key were added
93
+ current_claims.each_index do |index|
94
+ added_claims << { key: claim_key, index: index }
95
+ # check if there's any references or qualifiers in this claim
96
+ added_references = ReferenceAnalyzer.reference_updates(current_claims[index], added_references, claim_key, index)
97
+ added_qualifiers = QualifierAnalyzer.qualifier_updates(current_claims[index], added_qualifiers, claim_key, index)
98
+ end
221
99
  end
222
- # Check if the qualifier index exists in the current content
223
- if !current.nil?
224
- current = current[qualifier_index]
225
100
  end
226
- if current.nil?
227
- # Claim was removed
228
- removed_qualifiers << {
229
- claim_key: claim_key,
230
- claim_index: claim_index,
231
- qualifier_key: qualifier_key,
232
- qualifier_index: qualifier_index
233
- }
101
+
102
+ parent_content_claims.each do |claim_key, parent_claims|
103
+ # current content[claims] can be nil
104
+ parent_claims.each_index do |index|
105
+ if current_content_claims.nil? || !current_content_claims.key?(claim_key)
106
+ removed_claims << { key: claim_key, index: index }
107
+ # check if there's any references or qualifiers in this claim
108
+ removed_references = ReferenceAnalyzer.reference_updates(parent_claims[index], removed_references, claim_key, index)
109
+ removed_qualifiers = QualifierAnalyzer.qualifier_updates(parent_claims[index], removed_qualifiers, claim_key, index)
110
+ end
111
+ end
234
112
  end
235
113
  end
236
- end
237
114
 
238
115
  {
239
- added_qualifiers: added_qualifiers,
240
- removed_qualifiers: removed_qualifiers,
241
- changed_qualifiers: changed_qualifiers
116
+ added_claims: added_claims,
117
+ removed_claims: removed_claims,
118
+ changed_claims: changed_claims,
119
+ added_references: added_references,
120
+ removed_references: removed_references,
121
+ changed_references: changed_references,
122
+ added_qualifiers: added_qualifiers,
123
+ removed_qualifiers: removed_qualifiers,
124
+ changed_qualifiers: changed_qualifiers
242
125
  }
243
126
  end
244
127
  end
@@ -0,0 +1,49 @@
1
+ class CommentAnalyzer
2
+ def self.isolate_comment_differences(comment)
3
+ phrases = {
4
+ 'merge_to': 0,
5
+ 'merge_from': 0,
6
+ 'redirect': 0,
7
+ 'undo': 0,
8
+ 'restore': 0,
9
+ 'clear_item': 0,
10
+ 'create_item': 0,
11
+ }
12
+
13
+ if comment.nil?
14
+ return phrases
15
+ end
16
+
17
+ if comment.include?('wbmergeitems-from')
18
+ phrases[:merge_from] = 1
19
+ end
20
+
21
+ if comment.include?('wbmergeitems-to')
22
+ phrases[:merge_to] = 1
23
+ end
24
+
25
+ if comment.include?('wbcreateredirect')
26
+ phrases[:redirect] = 1
27
+ end
28
+
29
+ if comment.include?('undo:')
30
+ phrases[:undo] = 1
31
+ end
32
+
33
+ if comment.include?('restore:')
34
+ phrases[:restore] = 1
35
+ end
36
+
37
+ if comment.include?('wbeditentity-override')
38
+ phrases[:clear_item] = 1
39
+ end
40
+
41
+ # create-property, create-item, create-lexeme all includes this phrase
42
+ # so based on content model in revision analyzer, it is decided which one it is
43
+ if comment.include?('wbeditentity-create')
44
+ phrases[:create_item] = 1
45
+ end
46
+
47
+ return phrases
48
+ end
49
+ end
@@ -1,49 +1,71 @@
1
- class DescriptionAnalyzer
1
+ class DescriptionAnalyzer
2
2
  def self.isolate_descriptions_differences(current_content, parent_content)
3
3
  return {
4
- changed: [],
5
- removed: [],
6
- added: []
4
+ changed_descriptions: [],
5
+ removed_descriptions: [],
6
+ added_descriptions: []
7
7
  } if current_content.nil? && parent_content.nil?
8
-
9
- current_descriptions = current_content['descriptions'] || {}
10
- parent_descriptions = parent_content['descriptions'] || {}
11
8
 
9
+ if current_content
10
+ current_descriptions = current_content['descriptions']
11
+ if current_descriptions.nil? || current_descriptions.is_a?(Array)
12
+ current_descriptions = {}
13
+ end
14
+ else
15
+ current_descriptions = {}
16
+ end
17
+
18
+
19
+ if parent_content
20
+ parent_descriptions = parent_content['descriptions']
21
+ if parent_descriptions.nil? || parent_descriptions.is_a?(Array)
22
+ parent_descriptions = {}
23
+ end
24
+ else
25
+ parent_descriptions = {}
26
+ end
12
27
 
13
28
  changed_descriptions = [] # Initialize as an array
14
29
  removed_descriptions = [] # Initialize as an array
15
30
  added_descriptions = [] # Initialize as an array
16
-
17
- if !current_descriptions.is_a?(Hash) || !parent_descriptions.is_a?(Hash)
18
- return{
19
- changed: changed_descriptions,
20
- removed: removed_descriptions,
21
- added: added_descriptions
22
- }
23
- end
24
31
 
25
- # Iterate over each language in the current descriptions
26
- (current_descriptions || {}).each do |lang, current_description|
27
- parent_description = parent_descriptions[lang]
28
-
29
- if parent_description.nil?
30
- added_descriptions << { lang: lang }
31
- elsif current_description != parent_description
32
- changed_descriptions << { lang: lang }
33
- end
34
- end
35
-
36
- # Iterate over each language in the parent descriptions to find removed descriptions
37
- (parent_descriptions || {}).each do |lang, parent_description|
38
- if current_descriptions[lang].nil?
39
- removed_descriptions << { lang: lang }
40
- end
32
+
33
+ # if parentid is 0, add all current description as added and return it
34
+ if parent_content.nil?
35
+ if !current_descriptions.empty?
36
+ current_descriptions.each do |lang, description|
37
+ added_descriptions << { lang: lang }
38
+ end
39
+ end
40
+ return {
41
+ changed_descriptions: changed_descriptions,
42
+ removed_descriptions: removed_descriptions,
43
+ added_descriptions: added_descriptions
44
+ }
45
+ else
46
+ # Iterate over each language in the current descriptions
47
+ (current_descriptions).each do |lang, current_description|
48
+ # checking if the parent descriptions is empty
49
+ if parent_descriptions.empty?
50
+ added_descriptions << { lang: lang }
51
+ elsif parent_descriptions[lang].nil?
52
+ added_descriptions << { lang: lang }
53
+ elsif current_description != parent_descriptions[lang]
54
+ changed_descriptions << { lang: lang }
55
+ end
56
+ end
57
+
58
+ # Iterate over each language in the parent descriptions to find removed descriptions
59
+ (parent_descriptions).each do |lang, parent_description|
60
+ if current_descriptions.empty?
61
+ removed_descriptions << { lang: lang }
62
+ end
63
+ end
41
64
  end
42
-
43
65
  {
44
- changed: changed_descriptions,
45
- removed: removed_descriptions,
46
- added: added_descriptions
66
+ changed_descriptions: changed_descriptions,
67
+ removed_descriptions: removed_descriptions,
68
+ added_descriptions: added_descriptions
47
69
  }
48
70
  end
49
71
  end
@@ -0,0 +1,67 @@
1
+ require_relative 'representation_analyzer'
2
+ require_relative 'inside_claim_analyzer'
3
+ class FormAnalyzer
4
+ def self.isolate_forms_differences(current_content, parent_content)
5
+ added_forms = []
6
+ removed_forms = []
7
+ changed_forms = []
8
+ added_representations = []
9
+ removed_representations = []
10
+ changed_representations = []
11
+ added_formclaims = []
12
+ removed_formclaims = []
13
+ changed_formclaims = []
14
+
15
+ current_forms = current_content&.fetch("forms", []) || []
16
+ parent_forms = parent_content&.fetch("forms", []) || []
17
+
18
+ current_forms.each_with_index do |current_form, index|
19
+ parent_form = parent_forms[index]
20
+
21
+ if parent_form.nil?
22
+ # Claim was added
23
+ added_forms << { index: index }
24
+ elsif current_form
25
+ # Claim was changed
26
+ changed_forms << { index: index }
27
+ end
28
+
29
+ representations = RepresentationAnalyzer.isolate_representation_differences(current_form, parent_form)
30
+ added_representations += representations[:added]
31
+ removed_representations += representations[:removed]
32
+ changed_representations += representations[:changed]
33
+
34
+ formclaims = InsideClaimAnalyzer.isolate_inside_claim_differences(current_form, parent_form)
35
+ added_formclaims += formclaims[:added]
36
+ removed_formclaims += formclaims[:removed]
37
+ changed_formclaims += formclaims[:changed]
38
+ end
39
+
40
+ parent_forms.each_with_index do |parent_form, index|
41
+ current_form = current_forms[index]
42
+
43
+ if current_form.nil?
44
+ # Claim was removed
45
+ removed_forms << { index: index }
46
+
47
+ representations = RepresentationAnalyzer.isolate_representation_differences(nil, parent_form)
48
+ removed_representations += representations[:removed]
49
+
50
+ formclaims = InsideClaimAnalyzer.isolate_inside_claim_differences(nil, parent_form)
51
+ removed_formclaims += formclaims[:removed]
52
+ end
53
+ end
54
+
55
+ {
56
+ added_forms: added_forms,
57
+ removed_forms: removed_forms,
58
+ changed_forms: changed_forms,
59
+ added_representations: added_representations,
60
+ removed_representations: removed_representations,
61
+ changed_representations: changed_representations,
62
+ added_formclaims: added_formclaims,
63
+ removed_formclaims: removed_formclaims,
64
+ changed_formclaims: changed_formclaims
65
+ }
66
+ end
67
+ end
@@ -0,0 +1,71 @@
1
+ class GlossAnalyzer
2
+ def self.isolate_gloss_differences(current_content, parent_content)
3
+ return {
4
+ changed: [],
5
+ removed: [],
6
+ added: []
7
+ } if current_content.nil? && parent_content.nil?
8
+
9
+ if current_content
10
+ current_glosses = current_content['glosses']
11
+ if current_glosses.nil? || current_glosses.is_a?(Array)
12
+ current_glosses = {}
13
+ end
14
+ else
15
+ current_glosses = {}
16
+ end
17
+
18
+ if parent_content
19
+ parent_glosses = parent_content['glosses']
20
+ if parent_glosses.nil? || parent_glosses.is_a?(Array)
21
+ parent_glosses = {}
22
+ end
23
+ else
24
+ parent_glosses = {}
25
+ end
26
+
27
+
28
+
29
+ changed = []
30
+ removed = []
31
+ added = []
32
+
33
+ # if parentid is 0, then add all labels as added and return it
34
+ if parent_content.nil?
35
+ current_glosses.each do |lang, label|
36
+ added << { lang: lang }
37
+ end
38
+ return {
39
+ changed: changed,
40
+ removed: removed,
41
+ added: added
42
+ }
43
+ else
44
+
45
+
46
+ # Iterate over each language in the current labels
47
+ (current_glosses || {}).each do |lang, current_gloss|
48
+ parent_gloss = parent_glosses[lang]
49
+
50
+ if parent_gloss.nil?
51
+ added << { lang: lang }
52
+ elsif current_gloss != parent_gloss
53
+ changed << { lang: lang }
54
+ end
55
+ end
56
+
57
+ # Iterate over each language in the parent labels to find removed labels
58
+ (parent_glosses || {}).each do |lang, parent_gloss|
59
+ if current_glosses[lang].nil?
60
+ removed << { lang: lang }
61
+ end
62
+ end
63
+ end
64
+
65
+ {
66
+ changed: changed,
67
+ removed: removed,
68
+ added: added
69
+ }
70
+ end
71
+ end