amee 2.0.25

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