amee 2.0.31 → 2.0.32

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,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
- def start_date
38
- @start_date
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
- 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}")
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
- response = @connection.put(full_path, :value => value).body
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
- rescue
162
- raise AMEE::BadData.new("Couldn't delete DataItemValue. Check that your information is correct.")
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
@@ -1,9 +1,10 @@
1
1
  module AMEE
2
2
  module Data
3
3
  class Object < AMEE::Object
4
-
4
+
5
+ attr_accessor :path
5
6
  def full_path
6
- "/data#{@path}"
7
+ "/data#{path}"
7
8
  end
8
9
 
9
10
  end
@@ -23,5 +23,7 @@ module AMEE
23
23
 
24
24
  class UnknownError < Exception
25
25
  end
26
-
26
+
27
+ class NotSupported < Exception
28
+ end
27
29
  end
data/lib/amee/object.rb CHANGED
@@ -7,7 +7,7 @@ module AMEE
7
7
  @modified = data ? data[:modified] : @created
8
8
  @path = data ? data[:path] : nil
9
9
  @name = data ? data[:name] : nil
10
- @connection = nil
10
+ @connection = data ? data[:connection] : nil
11
11
  end
12
12
 
13
13
  attr_accessor :connection
data/lib/amee.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'rexml/document'
2
- require 'activesupport'
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.31
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: 2009-12-11 00:00:00 +00:00
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: json
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