amee 2.0.25

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.
@@ -0,0 +1,196 @@
1
+ require 'date'
2
+
3
+ module AMEE
4
+ module Data
5
+ class Category < AMEE::Data::Object
6
+
7
+ def initialize(data = {})
8
+ @children = data ? data[:children] : []
9
+ @items = data ? data[:items] : []
10
+ super
11
+ end
12
+
13
+ attr_reader :children
14
+ attr_reader :items
15
+
16
+ def self.from_json(json)
17
+ # Parse json
18
+ doc = JSON.parse(json)
19
+ data = {}
20
+ data[:uid] = doc['dataCategory']['uid']
21
+ data[:created] = DateTime.parse(doc['dataCategory']['created'])
22
+ data[:modified] = DateTime.parse(doc['dataCategory']['modified'])
23
+ data[:name] = doc['dataCategory']['name']
24
+ data[:path] = doc['path']
25
+ data[:children] = []
26
+ doc['children']['dataCategories'].each do |child|
27
+ category_data = {}
28
+ category_data[:name] = child['name']
29
+ category_data[:path] = child['path']
30
+ category_data[:uid] = child['uid']
31
+ data[:children] << category_data
32
+ end
33
+ data[:items] = []
34
+ if doc['children']['dataItems']['rows']
35
+ doc['children']['dataItems']['rows'].each do |item|
36
+ item_data = {}
37
+ item.each_pair do |key, value|
38
+ item_data[key.to_sym] = value
39
+ end
40
+ data[:items] << item_data
41
+ end
42
+ end
43
+ # Create object
44
+ Category.new(data)
45
+ rescue
46
+ raise AMEE::BadData.new("Couldn't load DataCategory from JSON data. Check that your URL is correct.\n#{json}")
47
+ end
48
+
49
+ def self.from_xml(xml)
50
+ # Parse XML
51
+ doc = REXML::Document.new(xml)
52
+ data = {}
53
+ data[:uid] = REXML::XPath.first(doc, "/Resources/DataCategoryResource/DataCategory/@uid").to_s
54
+ data[:created] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataCategoryResource/DataCategory/@created").to_s)
55
+ data[:modified] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataCategoryResource/DataCategory/@modified").to_s)
56
+ data[:name] = REXML::XPath.first(doc, '/Resources/DataCategoryResource/DataCategory/?ame').text
57
+ data[:path] = REXML::XPath.first(doc, '/Resources/DataCategoryResource//?ath').text || ""
58
+ data[:children] = []
59
+ REXML::XPath.each(doc, '/Resources/DataCategoryResource//Children/DataCategories/DataCategory') do |child|
60
+ category_data = {}
61
+ category_data[:name] = (child.elements['Name'] || child.elements['name']).text
62
+ category_data[:path] = (child.elements['Path'] || child.elements['path']).text
63
+ category_data[:uid] = child.attributes['uid'].to_s
64
+ data[:children] << category_data
65
+ end
66
+ data[:items] = []
67
+ REXML::XPath.each(doc, '/Resources/DataCategoryResource//Children/DataItems/DataItem') do |item|
68
+ item_data = {}
69
+ item_data[:uid] = item.attributes['uid'].to_s
70
+ item.elements.each do |element|
71
+ item_data[element.name.to_sym] = element.text
72
+ end
73
+ if item_data[:path].nil?
74
+ item_data[:path] = item_data[:uid]
75
+ end
76
+ data[:items] << item_data
77
+ end
78
+ # Create object
79
+ Category.new(data)
80
+ rescue
81
+ raise AMEE::BadData.new("Couldn't load DataCategory from XML data. Check that your URL is correct.\n#{xml}")
82
+ end
83
+
84
+ def self.get(connection, path, orig_options = {})
85
+ unless orig_options.is_a?(Hash)
86
+ raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
87
+ end
88
+ # Convert to AMEE options
89
+ options = orig_options.clone
90
+ if orig_options[:items_per_page]
91
+ options[:itemsPerPage] = orig_options[:items_per_page]
92
+ else
93
+ options[:itemsPerPage] = 10
94
+ end
95
+
96
+ # Load data from path
97
+ response = connection.get(path, options).body
98
+
99
+ # Parse data from response
100
+ if response.is_json?
101
+ cat = Category.from_json(response)
102
+ else
103
+ cat = Category.from_xml(response)
104
+ end
105
+ # Store connection in object for future use
106
+ cat.connection = connection
107
+ # Done
108
+ return cat
109
+ rescue
110
+ raise AMEE::BadData.new("Couldn't load DataCategory. Check that your URL is correct.\n#{response}")
111
+ end
112
+
113
+ def self.root(connection)
114
+ self.get(connection, '/data')
115
+ end
116
+
117
+ def child(child_path)
118
+ AMEE::Data::Category.get(connection, "#{full_path}/#{child_path}")
119
+ end
120
+
121
+ def drill
122
+ AMEE::Data::DrillDown.get(connection, "#{full_path}/drill")
123
+ end
124
+
125
+ def item(options)
126
+ # Search fields - from most specific to least specific
127
+ item = items.find{ |x| (x[:uid] && x[:uid] == options[:uid]) ||
128
+ (x[:name] && x[:name] == options[:name]) ||
129
+ (x[:path] && x[:path] == options[:path]) ||
130
+ (x[:label] && x[:label] == options[:label]) }
131
+ # Pass through some options
132
+ new_opts = {}
133
+ new_opts[:format] = options[:format] if options[:format]
134
+ item ? AMEE::Data::Item.get(connection, "#{full_path}/#{item[:path]}", new_opts) : nil
135
+ end
136
+
137
+ def self.create(category, options = {})
138
+
139
+ connection = category.connection
140
+ path = category.full_path
141
+
142
+ # Do we want to automatically fetch the item afterwards?
143
+ get_item = options.delete(:get_item)
144
+
145
+ get_item = true if get_item.nil?
146
+ # Store format if set
147
+ format = options[:format]
148
+ unless options.is_a?(Hash)
149
+ raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
150
+ end
151
+ # Send data to path
152
+ options[:newObjectType] = "DC"
153
+ response = connection.post(path, options)
154
+ if response['Location']
155
+ location = response['Location'].match("http://.*?(/.*)")[1]
156
+ else
157
+ category = Category.parse(connection, response.body)
158
+ location = category.full_path
159
+ end
160
+ if get_item == true
161
+ get_options = {}
162
+ get_options[:format] = format if format
163
+ return AMEE::Data::Category.get(connection, location, get_options)
164
+ else
165
+ return location
166
+ end
167
+ rescue
168
+ raise AMEE::BadData.new("Couldn't create DataCategory. Check that your information is correct.\n#{response}")
169
+ end
170
+
171
+ def self.delete(connection, path)
172
+ connection.delete(path)
173
+ rescue
174
+ raise AMEE::BadData.new("Couldn't delete DataCategory. Check that your information is correct.")
175
+ end
176
+
177
+ def self.update(connection, path, options = {})
178
+ # Do we want to automatically fetch the item afterwards?
179
+ get_item = options.delete(:get_item)
180
+ get_item = true if get_item.nil?
181
+ # Go
182
+ response = connection.put(path, options)
183
+ if get_item
184
+ if response.body.empty?
185
+ return Category.get(connection, path)
186
+ else
187
+ return Category.parse(connection, response.body)
188
+ end
189
+ end
190
+ rescue
191
+ raise AMEE::BadData.new("Couldn't update Data Category. Check that your information is correct.")
192
+ end
193
+
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,218 @@
1
+ module AMEE
2
+ module Data
3
+ class Item < AMEE::Data::Object
4
+
5
+ def initialize(data = {})
6
+ @values = data[:values]
7
+ @choices = data[:choices]
8
+ @label = data[:label]
9
+ @item_definition = data[:item_definition]
10
+ @total_amount = data[:total_amount]
11
+ @total_amount_unit = data[:total_amount_unit]
12
+ super
13
+ end
14
+
15
+ attr_reader :values
16
+ attr_reader :choices
17
+ attr_reader :label
18
+ attr_reader :item_definition
19
+ attr_reader :total_amount
20
+ attr_reader :total_amount_unit
21
+
22
+ def self.from_json(json)
23
+ # Read JSON
24
+ doc = JSON.parse(json)
25
+ data = {}
26
+ data[:uid] = doc['dataItem']['uid']
27
+ data[:created] = DateTime.parse(doc['dataItem']['created'])
28
+ data[:modified] = DateTime.parse(doc['dataItem']['modified'])
29
+ data[:name] = doc['dataItem']['name']
30
+ data[:path] = doc['path']
31
+ data[:label] = doc['dataItem']['label']
32
+ data[:item_definition] = doc['dataItem']['itemDefinition']['uid']
33
+ # Read v2 total
34
+ data[:total_amount] = doc['amount']['value'] rescue nil
35
+ data[:total_amount_unit] = doc['amount']['unit'] rescue nil
36
+ # Read v1 total
37
+ if data[:total_amount].nil?
38
+ data[:total_amount] = doc['amountPerMonth'] rescue nil
39
+ data[:total_amount_unit] = "kg/month"
40
+ end
41
+ # Get values
42
+ data[:values] = []
43
+ doc['dataItem']['itemValues'].each do |value|
44
+ value_data = {}
45
+ value_data[:name] = value['name']
46
+ value_data[:path] = value['path']
47
+ value_data[:value] = value['value']
48
+ value_data[:drill] = value['itemValueDefinition']['drillDown'] rescue nil
49
+ value_data[:uid] = value['uid']
50
+ data[:values] << value_data
51
+ end
52
+ # Get choices
53
+ data[:choices] = []
54
+ doc['userValueChoices']['choices'].each do |choice|
55
+ choice_data = {}
56
+ choice_data[:name] = choice['name']
57
+ choice_data[:value] = choice['value']
58
+ data[:choices] << choice_data
59
+ end
60
+ # Create object
61
+ Item.new(data)
62
+ rescue
63
+ raise AMEE::BadData.new("Couldn't load DataItem from JSON. Check that your URL is correct.\n#{json}")
64
+ end
65
+
66
+ def self.from_xml(xml)
67
+ # Parse data from response into hash
68
+ doc = REXML::Document.new(xml)
69
+ data = {}
70
+ data[:uid] = REXML::XPath.first(doc, "/Resources/DataItemResource/DataItem/@uid").to_s
71
+ data[:created] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataItemResource/DataItem/@created").to_s)
72
+ data[:modified] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataItemResource/DataItem/@modified").to_s)
73
+ data[:name] = (REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/Name') || REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/name')).text
74
+ data[:path] = (REXML::XPath.first(doc, '/Resources/DataItemResource/Path') || REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/path')).text
75
+ data[:label] = (REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/Label') || REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/label')).text
76
+ data[:item_definition] = REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/ItemDefinition/@uid').to_s
77
+ # Read v2 total
78
+ data[:total_amount] = REXML::XPath.first(doc, '/Resources/DataItemResource/Amount').text.to_f rescue nil
79
+ data[:total_amount_unit] = REXML::XPath.first(doc, '/Resources/DataItemResource/Amount/@unit').to_s rescue nil
80
+ # Read v1 total
81
+ if data[:total_amount].nil?
82
+ data[:total_amount] = REXML::XPath.first(doc, '/Resources/DataItemResource/AmountPerMonth').text.to_f rescue nil
83
+ data[:total_amount_unit] = "kg/month"
84
+ end
85
+ # Get values
86
+ data[:values] = []
87
+ REXML::XPath.each(doc, '/Resources/DataItemResource/DataItem/ItemValues/ItemValue') do |value|
88
+ value_data = {}
89
+ value_data[:name] = (value.elements['Name'] || value.elements['name']).text
90
+ value_data[:path] = (value.elements['Path'] || value.elements['path']).text
91
+ value_data[:value] = (value.elements['Value'] || value.elements['value']).text
92
+ value_data[:drill] = value.elements['ItemValueDefinition'].elements['DrillDown'].text == "false" ? false : true rescue nil
93
+ value_data[:uid] = value.attributes['uid'].to_s
94
+ data[:values] << value_data
95
+ end
96
+ # Get choices
97
+ data[:choices] = []
98
+ REXML::XPath.each(doc, '/Resources/DataItemResource/Choices/Choices/Choice') do |choice|
99
+ choice_data = {}
100
+ choice_data[:name] = (choice.elements['Name']).text
101
+ choice_data[:value] = (choice.elements['Value']).text || ""
102
+ data[:choices] << choice_data
103
+ end
104
+ # Create object
105
+ Item.new(data)
106
+ rescue
107
+ raise AMEE::BadData.new("Couldn't load DataItem from XML. Check that your URL is correct.\n#{xml}")
108
+ end
109
+
110
+
111
+ def self.get(connection, path, options = {})
112
+ # Load data from path
113
+ response = connection.get(path, options).body
114
+ AMEE::Data::Item.parse(connection, response)
115
+ rescue
116
+ raise AMEE::BadData.new("Couldn't load DataItem. Check that your URL is correct.\n#{response}")
117
+ end
118
+
119
+ def self.parse(connection, response)
120
+ # Parse data from response
121
+ if response.is_json?
122
+ item = Item.from_json(response)
123
+ else
124
+ item = Item.from_xml(response)
125
+ end
126
+ # Store connection in object for future use
127
+ item.connection = connection
128
+ # Done
129
+ return item
130
+ end
131
+
132
+ def self.create_batch_without_category(connection, category_path, items, options = {})
133
+ if connection.format == :json
134
+ options.merge! :profileItems => items
135
+ post_data = options.to_json
136
+ else
137
+ options.merge!({:DataItems => items})
138
+ post_data = options.to_xml(:root => "DataCategory", :skip_types => true, :skip_nil => true)
139
+ end
140
+ # Post to category
141
+ response = connection.raw_post(category_path, post_data).body
142
+ # Send back a category object containing all the created items
143
+ unless response.empty?
144
+ return AMEE::Data::Category.parse(connection, response)
145
+ else
146
+ return true
147
+ end
148
+ end
149
+
150
+ def self.create(category, options = {})
151
+ create_without_category(category.connection, category.full_path, options)
152
+ end
153
+
154
+ def self.create_without_category(connection, path, options = {})
155
+ # Do we want to automatically fetch the item afterwards?
156
+ get_item = options.delete(:get_item)
157
+ get_item = true if get_item.nil?
158
+ # Store format if set
159
+ format = options[:format]
160
+ unless options.is_a?(Hash)
161
+ raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
162
+ end
163
+ # Create a data item!
164
+ options[:newObjectType] = "DI"
165
+ # Send data to path
166
+ response = connection.post(path, options)
167
+ if response['Location']
168
+ location = response['Location'].match("http://.*?(/.*)")[1]
169
+ else
170
+ category = Category.parse(connection, response.body)
171
+ location = category.full_path + "/" + category.items[0][:path]
172
+ end
173
+ if get_item == true
174
+ get_options = {}
175
+ get_options[:format] = format if format
176
+ return AMEE::Data::Item.get(connection, location, get_options)
177
+ else
178
+ return location
179
+ end
180
+ rescue
181
+ raise AMEE::BadData.new("Couldn't create DataItem. Check that your information is correct.\n#{response}")
182
+ end
183
+
184
+ def self.update(connection, path, options = {})
185
+ # Do we want to automatically fetch the item afterwards?
186
+ get_item = options.delete(:get_item)
187
+ get_item = true if get_item.nil?
188
+ # Go
189
+ response = connection.put(path, options)
190
+ if get_item
191
+ if response.body.empty?
192
+ return Item.get(connection, path)
193
+ else
194
+ return Item.parse(connection, response.body)
195
+ end
196
+ end
197
+ rescue
198
+ raise AMEE::BadData.new("Couldn't update DataItem. Check that your information is correct.\n#{response}")
199
+ end
200
+
201
+ def update(options = {})
202
+ AMEE::Data::Item.update(connection, full_path, options)
203
+ end
204
+
205
+ def self.delete(connection, path)
206
+ connection.delete(path)
207
+ rescue
208
+ raise AMEE::BadData.new("Couldn't delete DataItem. Check that your information is correct.")
209
+ end
210
+
211
+ def value(name_or_path_or_uid)
212
+ val = values.find{ |x| x[:name] == name_or_path_or_uid || x[:path] == name_or_path_or_uid || x[:uid] == name_or_path_or_uid}
213
+ val ? val[:value] : nil
214
+ end
215
+
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,167 @@
1
+ module AMEE
2
+ module Data
3
+ class ItemValue < AMEE::Data::Object
4
+
5
+ def initialize(data = {})
6
+ @value = data ? data[:value] : nil
7
+ @type = data ? data[:type] : nil
8
+ @from_profile = data ? data[:from_profile] : nil
9
+ @from_data = data ? data[:from_data] : nil
10
+ @start_date = data ? data[:start_date] : nil
11
+ super
12
+ end
13
+
14
+ attr_reader :type
15
+
16
+ def value
17
+ case type
18
+ when "DECIMAL"
19
+ @value.to_f
20
+ else
21
+ @value
22
+ end
23
+ end
24
+
25
+ def value=(val)
26
+ @value = val
27
+ end
28
+
29
+ def from_profile?
30
+ @from_profile
31
+ end
32
+
33
+ def from_data?
34
+ @from_data
35
+ end
36
+
37
+ def start_date
38
+ @start_date
39
+ end
40
+
41
+ def self.from_json(json, path)
42
+ # Read JSON
43
+ doc = JSON.parse(json)['itemValue']
44
+ data = {}
45
+ data[:uid] = doc['uid']
46
+ data[:created] = DateTime.parse(doc['created'])
47
+ data[:modified] = DateTime.parse(doc['modified'])
48
+ data[:name] = doc['name']
49
+ data[:path] = path.gsub(/^\/data/, '')
50
+ data[:value] = doc['value']
51
+ data[:type] = doc['itemValueDefinition']['valueDefinition']['valueType']
52
+ data[:start_date] = DateTime.parse(doc['startDate']) rescue nil
53
+ # Create object
54
+ ItemValue.new(data)
55
+ rescue
56
+ raise AMEE::BadData.new("Couldn't load DataItemValue from JSON. Check that your URL is correct.\n#{json}")
57
+ end
58
+
59
+ def self.from_xml(xml, path)
60
+ # Read XML
61
+ doc = REXML::Document.new(xml)
62
+ data = {}
63
+ data[:uid] = REXML::XPath.first(doc, "/Resources/DataItemValueResource/ItemValue/@uid").to_s
64
+ data[:created] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataItemValueResource/ItemValue/@Created").to_s)
65
+ data[:modified] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataItemValueResource/ItemValue/@Modified").to_s)
66
+ data[:name] = REXML::XPath.first(doc, '/Resources/DataItemValueResource/ItemValue/Name').text
67
+ data[:path] = path.gsub(/^\/data/, '')
68
+ data[:value] = REXML::XPath.first(doc, '/Resources/DataItemValueResource/ItemValue/Value').text
69
+ data[:type] = REXML::XPath.first(doc, '/Resources/DataItemValueResource/ItemValue/ItemValueDefinition/ValueDefinition/ValueType').text
70
+ data[:from_profile] = REXML::XPath.first(doc, '/Resources/DataItemValueResource/ItemValue/ItemValueDefinition/FromProfile').text == "true" ? true : false
71
+ data[:from_data] = REXML::XPath.first(doc, '/Resources/DataItemValueResource/ItemValue/ItemValueDefinition/FromData').text == "true" ? true : false
72
+ data[:start_date] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataItemValueResource/ItemValue/StartDate").text) rescue nil
73
+ # Create object
74
+ ItemValue.new(data)
75
+ rescue
76
+ raise AMEE::BadData.new("Couldn't load DataItemValue from XML. Check that your URL is correct.\n#{xml}")
77
+ end
78
+
79
+ def self.get(connection, path)
80
+ # Load data from path
81
+ response = connection.get(path).body
82
+ # Parse data from response
83
+ data = {}
84
+ value = ItemValue.parse(connection, response, path)
85
+ # Done
86
+ return value
87
+ rescue
88
+ raise AMEE::BadData.new("Couldn't load DataItemValue. Check that your URL is correct.")
89
+ end
90
+
91
+ def save!
92
+ response = @connection.put(full_path, :value => value).body
93
+ end
94
+
95
+ def self.parse(connection, response, path)
96
+ if response.is_json?
97
+ value = ItemValue.from_json(response, path)
98
+ else
99
+ value = ItemValue.from_xml(response, path)
100
+ end
101
+ # Store connection in object for future use
102
+ value.connection = connection
103
+ # Done
104
+ return value
105
+ rescue
106
+ raise AMEE::BadData.new("Couldn't load DataItemValue. Check that your URL is correct.\n#{response}")
107
+ end
108
+
109
+ def self.create(data_item, options = {})
110
+ # Do we want to automatically fetch the item afterwards?
111
+ get_item = options.delete(:get_item)
112
+ get_item = true if get_item.nil?
113
+ # Store format if set
114
+ format = options[:format]
115
+ unless options.is_a?(Hash)
116
+ raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
117
+ end
118
+ # Set startDate
119
+ if (options[:start_date])
120
+ options[:startDate] = options[:start_date].xmlschema
121
+ options.delete(:start_date)
122
+ end
123
+
124
+ response = data_item.connection.post(data_item.full_path, options)
125
+ location = response['Location'].match("http://.*?(/.*)")[1]
126
+
127
+ if get_item == true
128
+ get_options = {}
129
+ get_options[:format] = format if format
130
+ return AMEE::Data::ItemValue.get(data_item.connection, location)
131
+ else
132
+ return location
133
+ end
134
+ rescue
135
+ raise AMEE::BadData.new("Couldn't create DataItemValue. Check that your information is correct.")
136
+ end
137
+
138
+ def self.update(connection, path, options = {})
139
+ # Do we want to automatically fetch the item afterwards?
140
+ get_item = options.delete(:get_item)
141
+ get_item = true if get_item.nil?
142
+ # Go
143
+ response = connection.put(path, options)
144
+ if get_item
145
+ if response.body.empty?
146
+ return AMEE::Data::ItemValue.get(connection, path)
147
+ else
148
+ return AMEE::Data::ItemValue.parse(connection, response.body)
149
+ end
150
+ end
151
+ rescue
152
+ raise AMEE::BadData.new("Couldn't update DataItemValue. Check that your information is correct.\n#{response}")
153
+ end
154
+
155
+ def update(options = {})
156
+ AMEE::Data::ItemValue.update(connection, full_path, options)
157
+ end
158
+
159
+ def self.delete(connection, path)
160
+ connection.delete(path)
161
+ rescue
162
+ raise AMEE::BadData.new("Couldn't delete DataItemValue. Check that your information is correct.")
163
+ end
164
+
165
+ end
166
+ end
167
+ end