amee 2.0.31 → 2.0.32
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/amee/data_item_value.rb +77 -27
- data/lib/amee/data_item_value_history.rb +167 -0
- data/lib/amee/data_object.rb +3 -2
- data/lib/amee/exceptions.rb +3 -1
- data/lib/amee/object.rb +1 -1
- data/lib/amee.rb +2 -1
- metadata +14 -3
data/lib/amee/data_item_value.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module AMEE
|
2
|
+
Epoch=DateTime.parse(Time.at(0).xmlschema).utc
|
2
3
|
module Data
|
3
4
|
class ItemValue < AMEE::Data::Object
|
4
5
|
|
@@ -34,17 +35,29 @@ module AMEE
|
|
34
35
|
@from_data
|
35
36
|
end
|
36
37
|
|
37
|
-
|
38
|
-
|
38
|
+
attr_accessor :start_date
|
39
|
+
attr_accessor :uid
|
40
|
+
|
41
|
+
def uid_path
|
42
|
+
# create a path which is safe for DIVHs by using the UID if one is avai
|
43
|
+
if uid
|
44
|
+
@path.split(/\//)[0..-2].push(uid).join('/')
|
45
|
+
else
|
46
|
+
@path
|
47
|
+
end
|
39
48
|
end
|
40
|
-
|
49
|
+
|
50
|
+
def full_uid_path
|
51
|
+
"/data#{uid_path}"
|
52
|
+
end
|
53
|
+
|
41
54
|
def self.from_json(json, path)
|
42
55
|
# Read JSON
|
43
|
-
doc = JSON.parse(json)['itemValue']
|
56
|
+
doc = json.is_a?(String) ? JSON.parse(json)['itemValue'] : json
|
44
57
|
data = {}
|
45
58
|
data[:uid] = doc['uid']
|
46
|
-
data[:created] = DateTime.parse(doc['created'])
|
47
|
-
data[:modified] = DateTime.parse(doc['modified'])
|
59
|
+
data[:created] = DateTime.parse(doc['created']) rescue nil
|
60
|
+
data[:modified] = DateTime.parse(doc['modified']) rescue nil
|
48
61
|
data[:name] = doc['name']
|
49
62
|
data[:path] = path.gsub(/^\/data/, '')
|
50
63
|
data[:value] = doc['value']
|
@@ -58,22 +71,27 @@ module AMEE
|
|
58
71
|
|
59
72
|
def self.from_xml(xml, path)
|
60
73
|
# Read XML
|
61
|
-
doc = REXML::Document.new(xml)
|
74
|
+
doc = xml.is_a?(String) ? REXML::Document.new(xml) : xml
|
62
75
|
data = {}
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
76
|
+
if REXML::XPath.match(doc,"descendant-or-self::ItemValue").length>1
|
77
|
+
raise AMEE::BadData.new("Couldn't load DataItemValue from XML. This is an item value history.\n#{xml}")
|
78
|
+
end
|
79
|
+
begin
|
80
|
+
data[:uid] = REXML::XPath.first(doc, "descendant-or-self::ItemValue/@uid").to_s
|
81
|
+
data[:created] = DateTime.parse(REXML::XPath.first(doc, "descendant-or-self::ItemValue/@Created").to_s) rescue nil
|
82
|
+
data[:modified] = DateTime.parse(REXML::XPath.first(doc, "descendant-or-self::ItemValue/@Modified").to_s) rescue nil
|
83
|
+
data[:name] = REXML::XPath.first(doc, 'descendant-or-self::ItemValue/Name').text
|
84
|
+
data[:path] = path.gsub(/^\/data/, '')
|
85
|
+
data[:value] = REXML::XPath.first(doc, 'descendant-or-self::ItemValue/Value').text
|
86
|
+
data[:type] = REXML::XPath.first(doc, 'descendant-or-self::ItemValue/ItemValueDefinition/ValueDefinition/ValueType').text
|
87
|
+
data[:from_profile] = false
|
88
|
+
data[:from_data] = true
|
89
|
+
data[:start_date] = DateTime.parse(REXML::XPath.first(doc, "descendant-or-self::ItemValue/StartDate").text) rescue nil
|
90
|
+
# Create object
|
91
|
+
ItemValue.new(data)
|
92
|
+
rescue
|
93
|
+
raise AMEE::BadData.new("Couldn't load DataItemValue from XML. Check that your URL is correct.\n#{xml}")
|
94
|
+
end
|
77
95
|
end
|
78
96
|
|
79
97
|
def self.get(connection, path)
|
@@ -89,7 +107,32 @@ module AMEE
|
|
89
107
|
end
|
90
108
|
|
91
109
|
def save!
|
92
|
-
|
110
|
+
if start_date
|
111
|
+
ItemValue.update(connection,full_uid_path,:value=>value,
|
112
|
+
:start_date=>start_date,:get_item=>false)
|
113
|
+
else
|
114
|
+
ItemValue.update(connection,full_uid_path,:value=>value,:get_item=>false)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def delete!
|
119
|
+
raise AMEE::BadData.new("Cannot delete initial value for time series") if start_date==AMEE::Epoch
|
120
|
+
ItemValue.delete @connection,full_uid_path
|
121
|
+
end
|
122
|
+
|
123
|
+
def create!
|
124
|
+
data_item_path=path.split(/\//)[0..-2].join('/')
|
125
|
+
data_item=AMEE::Data::Item.new
|
126
|
+
data_item.path=data_item_path
|
127
|
+
data_item.connection=connection
|
128
|
+
data_item.connection or raise "No connection to AMEE available"
|
129
|
+
if start_date
|
130
|
+
ItemValue.create(data_item,:start_date=>start_date,
|
131
|
+
@path.split(/\//).pop.to_sym => value,:get_item=>false)
|
132
|
+
else
|
133
|
+
ItemValue.create(data_item,
|
134
|
+
@path.split(/\//).pop.to_sym => value,:get_item=>false)
|
135
|
+
end
|
93
136
|
end
|
94
137
|
|
95
138
|
def self.parse(connection, response, path)
|
@@ -107,6 +150,7 @@ module AMEE
|
|
107
150
|
end
|
108
151
|
|
109
152
|
def self.create(data_item, options = {})
|
153
|
+
|
110
154
|
# Do we want to automatically fetch the item afterwards?
|
111
155
|
get_item = options.delete(:get_item)
|
112
156
|
get_item = true if get_item.nil?
|
@@ -115,15 +159,15 @@ module AMEE
|
|
115
159
|
unless options.is_a?(Hash)
|
116
160
|
raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
|
117
161
|
end
|
162
|
+
|
118
163
|
# Set startDate
|
119
164
|
if (options[:start_date])
|
120
165
|
options[:startDate] = options[:start_date].xmlschema
|
121
166
|
options.delete(:start_date)
|
122
167
|
end
|
123
|
-
|
168
|
+
|
124
169
|
response = data_item.connection.post(data_item.full_path, options)
|
125
170
|
location = response['Location'].match("http://.*?(/.*)")[1]
|
126
|
-
|
127
171
|
if get_item == true
|
128
172
|
get_options = {}
|
129
173
|
get_options[:format] = format if format
|
@@ -139,6 +183,11 @@ module AMEE
|
|
139
183
|
# Do we want to automatically fetch the item afterwards?
|
140
184
|
get_item = options.delete(:get_item)
|
141
185
|
get_item = true if get_item.nil?
|
186
|
+
# Set startDate
|
187
|
+
if (options[:start_date])
|
188
|
+
options[:startDate] = options[:start_date].xmlschema if options[:start_date]!=Epoch
|
189
|
+
options.delete(:start_date)
|
190
|
+
end
|
142
191
|
# Go
|
143
192
|
response = connection.put(path, options)
|
144
193
|
if get_item
|
@@ -155,11 +204,12 @@ module AMEE
|
|
155
204
|
def update(options = {})
|
156
205
|
AMEE::Data::ItemValue.update(connection, full_path, options)
|
157
206
|
end
|
158
|
-
|
207
|
+
|
208
|
+
|
159
209
|
def self.delete(connection, path)
|
160
210
|
connection.delete(path)
|
161
|
-
|
162
|
-
|
211
|
+
rescue
|
212
|
+
raise AMEE::BadData.new("Couldn't delete DataItemValue. Check that your information is correct.")
|
163
213
|
end
|
164
214
|
|
165
215
|
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module AMEE
|
4
|
+
module Data
|
5
|
+
class ItemValueHistory
|
6
|
+
|
7
|
+
def initialize(data = {})
|
8
|
+
@type = data ? data[:type] : nil
|
9
|
+
@path = data ? data[:path] : nil
|
10
|
+
@connection = data ? data[:connection] : nil
|
11
|
+
@values=data&&data[:values] ? data[:values] : []
|
12
|
+
self.series=data[:series] if data[:series]
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :values
|
16
|
+
attr_accessor :path # the IV path corresponding to the series, without the /data
|
17
|
+
|
18
|
+
def full_path
|
19
|
+
"/data#{path}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def series
|
23
|
+
values.map {|x|
|
24
|
+
[x.start_date.utc,x.value]
|
25
|
+
}.sort {|x,y|
|
26
|
+
x[0]<=>y[0]
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def times
|
31
|
+
values.map {|x|
|
32
|
+
x.start_date.utc
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def series=(newseries)
|
37
|
+
raise AMEE::BadData.new("Series must have initial (Epoch) value") unless newseries.any?{|x| x[0]==Epoch}
|
38
|
+
@values=newseries.map{|x|
|
39
|
+
AMEE::Data::ItemValue.new(:value=>x[1],
|
40
|
+
:start_date=>x[0],
|
41
|
+
:path=>path,
|
42
|
+
:connection=>connection,
|
43
|
+
:type=>type
|
44
|
+
)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def value_at(time)
|
49
|
+
selected=values.select{|x| x.start_date==time}
|
50
|
+
raise AMEE::BadData.new("Multiple data item values matching one time.") if selected.length >1
|
51
|
+
raise AMEE::BadData.new("No data item value for that time #{time}.") if selected.length ==0
|
52
|
+
selected[0]
|
53
|
+
end
|
54
|
+
|
55
|
+
def values_at(times)
|
56
|
+
times.map{|x| value_at(x)}
|
57
|
+
end
|
58
|
+
|
59
|
+
def connection=(con)
|
60
|
+
@connection=con
|
61
|
+
@values.each{|x| x.connection=con}
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_reader :connection
|
65
|
+
attr_reader :type
|
66
|
+
|
67
|
+
def self.from_json(json, path)
|
68
|
+
# Read JSON
|
69
|
+
data = {}
|
70
|
+
data[:path] = path.gsub(/^\/data/, '')
|
71
|
+
doc = JSON.parse(json)['itemValues']
|
72
|
+
doc=[JSON.parse(json)['itemValue']] unless doc
|
73
|
+
data[:values]=doc.map do |json_item_value|
|
74
|
+
ItemValue.from_json(json_item_value,path)
|
75
|
+
end
|
76
|
+
data[:type]=data[:values][0].type
|
77
|
+
# Create object
|
78
|
+
ItemValueHistory.new(data)
|
79
|
+
rescue
|
80
|
+
raise AMEE::BadData.new("Couldn't load DataItemValueHistory from JSON. Check that your URL is correct.\n#{json}")
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.from_xml(xml, path)
|
84
|
+
# Read XML
|
85
|
+
data = {}
|
86
|
+
data[:path] = path.gsub(/^\/data/, '')
|
87
|
+
doc = REXML::Document.new(xml)
|
88
|
+
valuedocs=REXML::XPath.match(doc, '//ItemValue')
|
89
|
+
raise if valuedocs.length==0
|
90
|
+
data[:values] = valuedocs.map do |xml_item_value|
|
91
|
+
ItemValue.from_xml(xml_item_value,path)
|
92
|
+
end
|
93
|
+
data[:type]=data[:values][0].type
|
94
|
+
# Create object
|
95
|
+
ItemValueHistory.new(data)
|
96
|
+
rescue
|
97
|
+
raise AMEE::BadData.new("Couldn't load DataItemValueHistory from XML. Check that your URL is correct.\n#{xml}")
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.get(connection, path)
|
101
|
+
# Load data from path
|
102
|
+
response = connection.get(path,:valuesPerPage=>2).body
|
103
|
+
# Parse data from response
|
104
|
+
data = {}
|
105
|
+
value = ItemValueHistory.parse(connection, response, path)
|
106
|
+
# Done
|
107
|
+
return value
|
108
|
+
rescue
|
109
|
+
raise AMEE::BadData.new("Couldn't load DataItemValueHistory. Check that your URL is correct.")
|
110
|
+
end
|
111
|
+
|
112
|
+
def save!
|
113
|
+
raise AMEE::BadData.new("Can't save without a path") unless @path
|
114
|
+
raise AMEE::BadData.new("Can't save without a connection") unless @connection
|
115
|
+
origin=ItemValueHistory.get(connection,full_path)
|
116
|
+
changes=compare(origin)
|
117
|
+
changes[:updates].each do |update|
|
118
|
+
# we've decided to identify these, but the version in the thing to be
|
119
|
+
# saved is probably home made, so copy over the uid
|
120
|
+
update.uid=origin.value_at(update.start_date).uid
|
121
|
+
update.save!
|
122
|
+
end
|
123
|
+
changes[:insertions].each do |insertion|
|
124
|
+
insertion.create!
|
125
|
+
end
|
126
|
+
changes[:deletions].each do |deletion|
|
127
|
+
deletion.delete!
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def delete!
|
132
|
+
# deprecated, as DI cannot exist without at least one point
|
133
|
+
raise AMEE::NotSupported.new("Cannot delete all of history: at least one data point must always exist.")
|
134
|
+
end
|
135
|
+
|
136
|
+
def create!
|
137
|
+
# deprecated, as DI cannot exist without at least one point
|
138
|
+
raise AMEE::NotSupported.new("Cannot create a Data Item Value History from scratch: at least one data point must exist when the DI is created")
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.parse(connection, response, path)
|
142
|
+
if response.is_json?
|
143
|
+
history = ItemValueHistory.from_json(response, path)
|
144
|
+
else
|
145
|
+
history = ItemValueHistory.from_xml(response, path)
|
146
|
+
end
|
147
|
+
# Store connection in object for future use
|
148
|
+
history.connection = connection
|
149
|
+
# Done
|
150
|
+
return history
|
151
|
+
rescue
|
152
|
+
raise AMEE::BadData.new("Couldn't load DataItemValueHistory. Check that your URL is correct.\n#{response}")
|
153
|
+
end
|
154
|
+
|
155
|
+
def compare(origin)
|
156
|
+
new=Set.new(times)
|
157
|
+
old=Set.new(origin.times)
|
158
|
+
{
|
159
|
+
:insertions=>values_at(new-old),
|
160
|
+
:deletions=>origin.values_at(old-new),
|
161
|
+
:updates=>values_at(old&new)
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
data/lib/amee/data_object.rb
CHANGED
data/lib/amee/exceptions.rb
CHANGED
data/lib/amee/object.rb
CHANGED
data/lib/amee.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'rexml/document'
|
2
|
-
require '
|
2
|
+
require 'active_support'
|
3
3
|
|
4
4
|
# We don't NEED the JSON gem, but if it's available, use it.
|
5
5
|
begin
|
@@ -35,6 +35,7 @@ require 'amee/profile_object'
|
|
35
35
|
require 'amee/data_category'
|
36
36
|
require 'amee/data_item'
|
37
37
|
require 'amee/data_item_value'
|
38
|
+
require 'amee/data_item_value_history'
|
38
39
|
require 'amee/profile'
|
39
40
|
require 'amee/profile_category'
|
40
41
|
require 'amee/profile_item'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.32
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Smith
|
@@ -9,13 +9,23 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-02-26 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
17
17
|
type: :runtime
|
18
18
|
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.3.5
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
19
29
|
version_requirements: !ruby/object:Gem::Requirement
|
20
30
|
requirements:
|
21
31
|
- - ">="
|
@@ -23,7 +33,7 @@ dependencies:
|
|
23
33
|
version: "0"
|
24
34
|
version:
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
|
-
name:
|
36
|
+
name: rspec_spinner
|
27
37
|
type: :runtime
|
28
38
|
version_requirement:
|
29
39
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -55,6 +65,7 @@ files:
|
|
55
65
|
- lib/amee/version.rb
|
56
66
|
- lib/amee/data_category.rb
|
57
67
|
- lib/amee/data_item_value.rb
|
68
|
+
- lib/amee/data_item_value_history.rb
|
58
69
|
- lib/amee/data_object.rb
|
59
70
|
- lib/amee/object.rb
|
60
71
|
- lib/amee/shell.rb
|