medic 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/README.md +210 -10
- data/lib/medic.rb +1 -3
- data/lib/medic/anchor.rb +32 -0
- data/lib/medic/anchored_object_query.rb +18 -0
- data/lib/medic/correlation_query.rb +22 -0
- data/lib/medic/finders.rb +122 -0
- data/lib/medic/hk_constants.rb +76 -0
- data/lib/medic/interval.rb +29 -0
- data/lib/medic/medic.rb +68 -1
- data/lib/medic/observer_query.rb +15 -0
- data/lib/medic/predicate.rb +39 -0
- data/lib/medic/query_options.rb +23 -0
- data/lib/medic/sample_query.rb +22 -0
- data/lib/medic/sort.rb +24 -0
- data/lib/medic/source_query.rb +15 -0
- data/lib/medic/statistics_collection_query.rb +39 -0
- data/lib/medic/statistics_options.rb +31 -0
- data/lib/medic/statistics_query.rb +17 -0
- data/lib/medic/store.rb +117 -0
- data/lib/medic/types.rb +100 -0
- data/lib/medic/version.rb +1 -1
- data/spec/medic/anchor.rb +16 -0
- data/spec/medic/anchored_object_query_spec.rb +12 -0
- data/spec/medic/correlation_query_spec.rb +16 -0
- data/spec/medic/hk_constants_spec.rb +67 -0
- data/spec/medic/interval_spec.rb +16 -0
- data/spec/medic/medic_spec.rb +153 -0
- data/spec/medic/observer_query_spec.rb +12 -0
- data/spec/medic/predicate_spec.rb +27 -0
- data/spec/medic/query_options_spec.rb +24 -0
- data/spec/medic/sample_query_spec.rb +12 -0
- data/spec/medic/sort_spec.rb +26 -0
- data/spec/medic/source_query_spec.rb +12 -0
- data/spec/medic/statistics_collection_query_spec.rb +27 -0
- data/spec/medic/statistics_options_spec.rb +32 -0
- data/spec/medic/statistics_query_spec.rb +12 -0
- data/spec/medic/store_spec.rb +159 -0
- data/spec/medic/types_spec.rb +95 -0
- metadata +72 -9
- data/spec/main_spec.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf3a75beba5a3f3be3df75476d6d18d188752bac
|
4
|
+
data.tar.gz: 01bb258ba550c735090e7d04df792972562f4f91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02ff5adbb1f076e1e95fee777d4d5289e0bd1eb73808f32d3ee90a6b2a45aafc27b13e323ea96885e55294e4356d2eb783cbd02e7b271287a7dc1afa07df7fa1
|
7
|
+
data.tar.gz: 780718864993970ea3889ac42df1627066152d3862aab9be21f7f5eded7b146195874fff3c1a68497dc46b14482aa5348f089c70eac31e9572250511ee34ca8e
|
data/README.md
CHANGED
@@ -1,24 +1,224 @@
|
|
1
|
-
# medic
|
1
|
+
# Medic [](http://badge.fury.io/rb/medic) [](https://travis-ci.org/ryanlntn/medic) [](https://codeclimate.com/github/ryanlntn/medic) [](https://gemnasium.com/ryanlntn/medic)
|
2
2
|
|
3
|
-
|
3
|
+
Is HealthKit's verbose and convoluted API driving you to autolobotomization? Quick! You need a medic!
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
Add this line to your application's Gemfile:
|
7
|
+
1. Add this line to your application's Gemfile:
|
8
8
|
|
9
|
-
|
9
|
+
```ruby
|
10
|
+
gem 'medic'
|
11
|
+
```
|
10
12
|
|
11
|
-
|
13
|
+
1. Add the following lines to your Rakefile:
|
12
14
|
|
13
|
-
|
15
|
+
```ruby
|
16
|
+
app.entitlements['com.apple.developer.healthkit'] = true
|
17
|
+
app.frameworks += ['HealthKit']
|
18
|
+
```
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
$ gem install medic
|
20
|
+
1. Run `bundle` and `rake`.
|
18
21
|
|
19
22
|
## Usage
|
20
23
|
|
21
|
-
|
24
|
+
### Authorization
|
25
|
+
|
26
|
+
To request authorization to read or share (i.e. write) a data type implement the following in `viewDidAppear` of your `UIViewController`:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
if Medic.available?
|
30
|
+
types = { share: :step_count, read: [:step_count, :date_of_birth] }
|
31
|
+
|
32
|
+
Medic.authorize types do |success, error|
|
33
|
+
NSLog "Success!" if success
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
This will open the permissions modal. `success` will be `false` if the user canceled the prompt without selecting permissions; `true` otherwise.
|
39
|
+
|
40
|
+
You can subsequently check if you're authorized to share a data type:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
Medic.authorized?(:step_count)
|
44
|
+
```
|
45
|
+
|
46
|
+
Note: For privacy reasons Apple does not allow you to check if you're authorized to read data types.
|
47
|
+
|
48
|
+
### Sharing Samples
|
49
|
+
|
50
|
+
Coming soon...
|
51
|
+
|
52
|
+
### Reading Data
|
53
|
+
|
54
|
+
HealthKit provides a number of methods for accessing its data, mostly in the form of query objects with verbose initializers that return more `HKObject`s with repetitive method names. For example, if I wanted to get the total number of steps taken per day for the last week I could use a `HKStatisticsCollectionQuery` like so:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
@store = HKHealthStore.new
|
58
|
+
|
59
|
+
today = NSDate.date
|
60
|
+
one_week_ago = NSCalendar.currentCalendar.dateByAddingComponents(NSDateComponents.new.setDay(-7), toDate: today, options: 0)
|
61
|
+
|
62
|
+
query = HKStatisticsCollectionQuery.initWithQuantityType(
|
63
|
+
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount),
|
64
|
+
quantitySamplePredicate: nil,
|
65
|
+
options: HKStatisticsOptionCumulativeSum,
|
66
|
+
anchorDate: one_week_ago,
|
67
|
+
intervalComponents: (NSDateComponents.new.day = 1)
|
68
|
+
)
|
69
|
+
|
70
|
+
query.initialResultsHandler = ->(query, results, error){
|
71
|
+
results.enumerateStatisticsFromDate(one_week_ago, toDate: today, withBlock: ->(result, stop){
|
72
|
+
if quantity = result.sumQuantity
|
73
|
+
NSLog quantity.doubleValueForUnit(HKUnit.countUnit).to_s
|
74
|
+
end
|
75
|
+
})
|
76
|
+
}
|
77
|
+
|
78
|
+
@store.executeQuery(query)
|
79
|
+
```
|
80
|
+
|
81
|
+
This doesn't make for the most readable code. As a Ruby developer you might find this downright distasteful. Let's check out the Medic equivalent:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
options = { options: :sum, anchor: :one_week_ago, interval: :day}
|
85
|
+
|
86
|
+
Medic.find_statistics_collection :step_count, options do |statistics|
|
87
|
+
statistics.each do |stats|
|
88
|
+
NSLog stats[:sum].to_s
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
Now that's more like it! Instead of constructing `HKObjectType` by hand we can now just pass in a symbol. We also don't have to work directly with `HKStatisticsCollection` anymore. The result is parsed into an array of hashes with reasonable values for us.
|
94
|
+
|
95
|
+
#### Finders
|
96
|
+
|
97
|
+
Medic provides a finder for each class of `HKQuery`:
|
98
|
+
|
99
|
+
##### find_anchored
|
100
|
+
|
101
|
+
Provide an easy way to search for new data in the HealthKit store.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
@anchor = nil
|
105
|
+
|
106
|
+
Medic.find_anchored :step_count, anchor: @anchor do |results, new_anchor|
|
107
|
+
@anchor = new_anchor
|
108
|
+
results.each do |sample|
|
109
|
+
NSLog sample.to_s
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
##### find_correlation
|
115
|
+
|
116
|
+
Search for correlations in the HealthKit store.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
high_cal = HKQuantity.quantityWithUnit(HKUnit.kilocalorieUnit, doubleValue: 800.0)
|
120
|
+
greater_than_high_cal = HKQuery.predicateForQuantitySamplesWithOperatorType(NSGreaterThanOrEqualToPredicateOperatorType, quantity: high_cal)
|
121
|
+
|
122
|
+
sample_predicates = { dietary_energy_consumed: greater_than_high_cal }
|
123
|
+
|
124
|
+
Medic.find_correlations :food, sample_predicates: sample_predicates do |correlations|
|
125
|
+
correlations.each do |correlation|
|
126
|
+
NSLog correlation.to_s
|
127
|
+
end
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
##### observe
|
132
|
+
|
133
|
+
Set up a long-running task on a background queue.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
Medic.observe :step_count do |completion, error|
|
137
|
+
Medic.find_sources :step_count do |sources|
|
138
|
+
sources.each do |source|
|
139
|
+
NSLog source
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
##### find_samples
|
146
|
+
|
147
|
+
Search for sample data in the HealthKit store.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
Medic.find_samples :blood_pressure, sort: :start_date, limit: 7 do |samples|
|
151
|
+
samples.each do |sample|
|
152
|
+
NSLog sample.to_s
|
153
|
+
end
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
##### find_sources
|
158
|
+
|
159
|
+
Search for the sources (apps and devices) that have saved data to the HealthKit store.
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
Medic.find_sources :step_count do |sources|
|
163
|
+
sources.each do |source|
|
164
|
+
NSLog source
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
##### find_statistics
|
170
|
+
|
171
|
+
Perform statistical calculations over the set of matching quantity samples.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
Medic.find_statistics :step_count, options: :sum do |statistics|
|
175
|
+
NSLog statistics.to_s
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
##### find_statistics_collection
|
180
|
+
|
181
|
+
Perform multiple statistics queries over a series of fixed-length time intervals.
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
options = { options: :sum, anchor: :one_week_ago, interval: :day}
|
185
|
+
|
186
|
+
Medic.find_statistics_collection :step_count, options do |statistics|
|
187
|
+
statistics.each do |stats|
|
188
|
+
NSLog stats[:sum].to_s
|
189
|
+
end
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
#### Characteristic Data
|
194
|
+
|
195
|
+
Characteristic data like biological sex or blood type have their own methods:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
Medic.biological_sex # => :male
|
199
|
+
Medic.date_of_birth # => 1987-11-07 00:00:00 -0800
|
200
|
+
Medic.blood_type # => :o_negative
|
201
|
+
```
|
202
|
+
|
203
|
+
#### Queries
|
204
|
+
|
205
|
+
If for some reason you need to access the `HKSample` objects directly you can use Medic's Query objects:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
query_params = { type: :dietary_protein, sort_desc: :start_date, limit: 7 }
|
209
|
+
|
210
|
+
query = Medic::SampleQuery.new query_params do |query, results, error|
|
211
|
+
if results
|
212
|
+
results.each do |sample|
|
213
|
+
NSLog "#{sample.startDate} - #{sample.quantity.doubleValueForUnit(HKUnit.gramUnit)}"
|
214
|
+
end
|
215
|
+
else
|
216
|
+
NSLog "no results"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
Medic.execute(query)
|
221
|
+
```
|
22
222
|
|
23
223
|
## Contributing
|
24
224
|
|
data/lib/medic.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
unless defined?(Motion::Project::Config)
|
4
2
|
raise "This file must be required within a RubyMotion project Rakefile."
|
5
3
|
end
|
6
4
|
|
7
5
|
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
8
6
|
Motion::Project::App.setup do |app|
|
9
|
-
app.files.unshift(Dir.glob(File.join(lib_dir_path, "
|
7
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "medic/**/*.rb")))
|
10
8
|
end
|
data/lib/medic/anchor.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Medic
|
2
|
+
module Anchor
|
3
|
+
|
4
|
+
NUMBER_WORDS = {
|
5
|
+
'zero' => 0, 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'five' => 5,
|
6
|
+
'six' => 6, 'seven' => 7, 'eight' => 8, 'nine' => 9, 'ten' => 10, 'eleven' => 11,
|
7
|
+
'twelve' => 12, 'thirteen' => 13, 'fourteen' => 14, 'fifteen' => 15, 'sixteen' => 16,
|
8
|
+
'seventeen' => 17, 'eighteen' => 18, 'nineteen' => 19, 'twenty' => 20, 'thirty' => 30,
|
9
|
+
'fourty' => 40, 'fifty' => 50, 'sixty' => 60, 'seventy' => 70, 'eighty' => 80,
|
10
|
+
'ninety' => 90, 'hundred' => 100
|
11
|
+
}
|
12
|
+
|
13
|
+
def anchor_for_symbol(sym)
|
14
|
+
return unless sym
|
15
|
+
return sym if sym.to_s == '0'
|
16
|
+
return sym if sym.is_a? NSDate
|
17
|
+
parts = sym.to_s.gsub('_', ' ').split.reject{ |part| part == 'ago' }
|
18
|
+
component = parts.pop.chomp('s')
|
19
|
+
n = parts.map{|p| NUMBER_WORDS[p] || p.to_i}.reduce do |sum, p|
|
20
|
+
if p == 100 && sum > 0
|
21
|
+
sum * p
|
22
|
+
else
|
23
|
+
sum + p
|
24
|
+
end
|
25
|
+
end
|
26
|
+
n ||= 1
|
27
|
+
date_comp = NSDateComponents.new.send("#{component}=", -n)
|
28
|
+
NSCalendar.currentCalendar.dateByAddingComponents(date_comp, toDate: NSDate.date, options: 0)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Medic
|
2
|
+
class AnchoredObjectQuery < HKAnchoredObjectQuery
|
3
|
+
|
4
|
+
include Medic::Types
|
5
|
+
include Medic::Predicate
|
6
|
+
include Medic::Anchor
|
7
|
+
|
8
|
+
def initialize(args={}, block=Proc.new)
|
9
|
+
self.initWithType(object_type(args[:type]),
|
10
|
+
predicate: predicate(args),
|
11
|
+
anchor: anchor_for_symbol(args[:anchor_date] || args[:anchor] || args[:date] || HKAnchoredObjectQueryNoAnchor),
|
12
|
+
limit: args[:limit] || HKObjectQueryNoLimit,
|
13
|
+
completionHandler: block
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Medic
|
2
|
+
class CorrelationQuery < HKCorrelationQuery
|
3
|
+
|
4
|
+
include Medic::Types
|
5
|
+
include Medic::Predicate
|
6
|
+
|
7
|
+
def initialize(args={}, block=Proc.new)
|
8
|
+
self.initWithType(object_type(args[:type]),
|
9
|
+
predicate: predicate(args),
|
10
|
+
samplePredicates: sample_predicates(args[:sample_predicates]),
|
11
|
+
completion: block
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def sample_predicates(predicates)
|
18
|
+
Hash[ predicates.map{ |type, pred| [object_type(type), predicate(pred)] } ]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Medic
|
2
|
+
module Finders
|
3
|
+
|
4
|
+
def observe(type, options={}, block=Proc.new)
|
5
|
+
query_params = options.merge(type: type)
|
6
|
+
query = Medic::ObserverQuery.new query_params do |query, completion, error|
|
7
|
+
block.call(completion, error)
|
8
|
+
end
|
9
|
+
Medic.execute(query)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_sources(type, options={}, block=Proc.new)
|
13
|
+
query_params = options.merge(type: type)
|
14
|
+
query = Medic::SourceQuery.new query_params do |query, results, error|
|
15
|
+
sources = results ? results.allObjects.map{ |source| source.name.to_s } : []
|
16
|
+
block.call(sources)
|
17
|
+
end
|
18
|
+
Medic.execute(query)
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_samples(type, options={}, block=Proc.new)
|
22
|
+
query_params = options.merge(type: type)
|
23
|
+
query = Medic::SampleQuery.new query_params do |query, results, error|
|
24
|
+
block.call(samples_to_hashes(Array(results)))
|
25
|
+
end
|
26
|
+
Medic.execute(query)
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_correlations(type, options={}, block=Proc.new)
|
30
|
+
query_params = options.merge(type: type)
|
31
|
+
query = Medic::CorrelationQuery.new query_params do |query, correlations, error|
|
32
|
+
block.call(samples_to_hashes(Array(correlations)))
|
33
|
+
end
|
34
|
+
Medic.execute(query)
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_anchored(type, options={}, block=Proc.new)
|
38
|
+
query_params = options.merge(type: type)
|
39
|
+
query = Medic::AnchoredObjectQuery.new query_params do |query, results, new_anchor, error|
|
40
|
+
block.call(samples_to_hashes(Array(results)), new_anchor)
|
41
|
+
end
|
42
|
+
Medic.execute(query)
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_statistics(type, options={}, block=Proc.new)
|
46
|
+
query_params = options.merge(type: type)
|
47
|
+
query = Medic::StatisticsQuery.new query_params do |query, statistics, error|
|
48
|
+
block.call(statistics_to_hash(statistics)) if statistics
|
49
|
+
end
|
50
|
+
Medic.execute(query)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_statistics_collection(type, options={}, block=Proc.new)
|
54
|
+
query_params = options.merge(type: type)
|
55
|
+
query = Medic::StatisticsCollectionQuery.new query_params do |query, collection, error|
|
56
|
+
formatted_stats = []
|
57
|
+
collection.enumerateStatisticsFromDate(collection.anchorDate, toDate: NSDate.date, withBlock: ->(result, stop){
|
58
|
+
formatted_stats << statistics_to_hash(result)
|
59
|
+
})
|
60
|
+
block.call(formatted_stats)
|
61
|
+
end
|
62
|
+
Medic.execute(query)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def samples_to_hashes(samples)
|
68
|
+
samples.map do |sample|
|
69
|
+
h = {}
|
70
|
+
h[:uuid] = sample.UUID.UUIDString
|
71
|
+
h[:metadata] = sample.metadata
|
72
|
+
h[:source] = sample.source.name
|
73
|
+
h[:start_date] = sample.startDate
|
74
|
+
h[:end_date] = sample.endDate
|
75
|
+
h[:sample_type] = Medic::Types::TYPE_IDENTIFIERS.index(sample.sampleType.identifier)
|
76
|
+
|
77
|
+
if sample.respond_to?(:categoryType) && sample.respond_to?(:value)
|
78
|
+
h[:category_type] = Medic::Types::TYPE_IDENTIFIERS.index(sample.categoryType.identifier)
|
79
|
+
h[:value] = [:in_bed, :asleep][sample.value] # SleepAnalysis is the only category type at the moment
|
80
|
+
end
|
81
|
+
|
82
|
+
if sample.respond_to?(:correlationType) && sample.respond_to?(:objects)
|
83
|
+
h[:correlation_type] = Medic::Types::TYPE_IDENTIFIERS.index(sample.correlationType.identifier)
|
84
|
+
h[:objects] = samples_to_hashes(Array(sample.objects.allObjects))
|
85
|
+
end
|
86
|
+
|
87
|
+
if sample.respond_to?(:quantity) && sample.respond_to?(:quantityType)
|
88
|
+
h[:quantity] = sample.quantity.doubleValueForUnit(sample.quantityType.canonicalUnit)
|
89
|
+
h[:quantity_type] = Medic::Types::TYPE_IDENTIFIERS.index(sample.quantityType.identifier)
|
90
|
+
h[:canonical_unit] = sample.quantityType.canonicalUnit.unitString
|
91
|
+
end
|
92
|
+
|
93
|
+
h[:duration] = sample.duration if sample.respond_to?(:duration)
|
94
|
+
h[:total_distance] = sample.totalDistance if sample.respond_to?(:totalDistance)
|
95
|
+
h[:total_energy_burned] = sample.totalEnergyBurned if sample.respond_to?(:totalEnergyBurned)
|
96
|
+
h[:workout_activity_type] = sample.workoutActivityType if sample.respond_to?(:workoutActivityType)
|
97
|
+
h[:workout_events] = sample.workoutEvents if sample.respond_to?(:workoutEvents)
|
98
|
+
h
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def statistics_to_hash(stats)
|
103
|
+
h = {}
|
104
|
+
h[:start_date] = stats.startDate
|
105
|
+
h[:end_date] = stats.endDate
|
106
|
+
h[:sources] = stats.sources.map(&:name) if stats.sources
|
107
|
+
h[:quantity_type] = Medic::Types::TYPE_IDENTIFIERS.index(stats.quantityType.identifier)
|
108
|
+
h[:canonical_unit] = stats.quantityType.canonicalUnit.unitString
|
109
|
+
h[:data_count] = stats.dataCount
|
110
|
+
h[:average] = stats.averageQuantity.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.averageQuantity
|
111
|
+
h[:minimum] = stats.minimumQuantity.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.minimumQuantity
|
112
|
+
h[:maximum] = stats.maximumQuantity.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.maximumQuantity
|
113
|
+
h[:sum] = stats.sumQuantity.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.sumQuantity
|
114
|
+
h[:average_by_source] = stats.averageQuantityBySource.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.averageQuantityBySource
|
115
|
+
h[:minimum_by_source] = stats.minimumQuantityBySource.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.minimumQuantityBySource
|
116
|
+
h[:maximum_by_source] = stats.maximumQuantityBySource.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.maximumQuantityBySource
|
117
|
+
h[:sum_by_source] = stats.sumQuantityBySource.doubleValueForUnit(stats.quantityType.canonicalUnit) if stats.sumQuantityBySource
|
118
|
+
h
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|