dhis2 0.1.0 → 2.1.0
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 +73 -23
- data/lib/dhis2/api/analytic.rb +16 -0
- data/lib/dhis2/api/base.rb +104 -0
- data/lib/dhis2/api/category_combo.rb +26 -0
- data/lib/dhis2/api/data_element.rb +31 -0
- data/lib/dhis2/api/data_element_group.rb +22 -0
- data/lib/dhis2/api/data_set.rb +25 -0
- data/lib/dhis2/api/data_value.rb +13 -0
- data/lib/dhis2/api/data_value_set.rb +39 -0
- data/lib/dhis2/api/indicator.rb +6 -0
- data/lib/dhis2/api/organisation_unit.rb +68 -0
- data/lib/dhis2/api/organisation_unit_group.rb +11 -0
- data/lib/dhis2/api/organisation_unit_level.rb +6 -0
- data/lib/dhis2/client.rb +114 -0
- data/lib/dhis2/collection_wrapper.rb +13 -0
- data/lib/dhis2/configuration.rb +5 -0
- data/lib/dhis2/import_error.rb +4 -0
- data/lib/dhis2/pager.rb +10 -0
- data/lib/dhis2/paginated_array.rb +10 -0
- data/lib/dhis2/status.rb +34 -0
- data/lib/dhis2/version.rb +1 -1
- data/lib/dhis2.rb +43 -34
- metadata +21 -10
- data/lib/base.rb +0 -49
- data/lib/data_element.rb +0 -39
- data/lib/data_set.rb +0 -35
- data/lib/organisation_unit.rb +0 -14
- data/lib/organisation_unit_level.rb +0 -19
- data/lib/pager.rb +0 -10
- data/lib/paginated_array.rb +0 -11
- data/lib/status.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed649a064de87ceea944479aa82f20e239e912ee
|
4
|
+
data.tar.gz: 862e2be603b90d7da556faae38ba8df02ee51759
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ccb72a75795cd3c0e9d1b863ea25c93181410d09cf03dacafebe19b25c84a99defc1da3aaee01236b61f530f9381b4c177a2cb2c9ae608d7d046cf9cb0c9f39
|
7
|
+
data.tar.gz: 82a608fd72682b034e53b4df5107474ea4b89b14bc93968829398b55499f2abfa05afd7bedded231be603bff9697c33195087ee63fe49eb2694fd48c79224bc5
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Dhis2
|
2
2
|
|
3
|
+
<a href="https://codeclimate.com/github/BLSQ/dhis2"><img src="https://codeclimate.com/github/BLSQ/dhis2/badges/gpa.svg" /></a>
|
4
|
+
|
3
5
|
Basic DHIS2 API client for Ruby.
|
4
6
|
|
5
7
|
## Installation
|
@@ -7,7 +9,8 @@ Basic DHIS2 API client for Ruby.
|
|
7
9
|
Add this line to your application's Gemfile:
|
8
10
|
|
9
11
|
```ruby
|
10
|
-
gem 'dhis2'
|
12
|
+
gem 'dhis2' # get it from RubyGems
|
13
|
+
gem 'dhis2', github: 'BLSQ/dhis2' # OR get the bleeding edge version from github
|
11
14
|
```
|
12
15
|
|
13
16
|
And then execute:
|
@@ -24,21 +27,47 @@ Or install it yourself as:
|
|
24
27
|
|
25
28
|
The functionalities are available as a module. First thing you need to do is to connect to the instance:
|
26
29
|
|
27
|
-
|
30
|
+
* Global configuration (to call a single Dhis2 instance):
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
Dhis2.configure do |config|
|
34
|
+
config.url = "https://play.dhis2.org/demo"
|
35
|
+
config.user = "admin"
|
36
|
+
config.password = "district"
|
37
|
+
end
|
38
|
+
Dhis2.client.data_elements.list # => Array[<DataElement>,..]
|
39
|
+
|
40
|
+
# Or alternatively
|
41
|
+
Dhis2.configure do |config|
|
42
|
+
config.url = "https://admin:district@play.dhis2.org/demo"
|
43
|
+
end
|
44
|
+
Dhis2.client.data_elements.list # => Array[<DataElement>,..]
|
45
|
+
```
|
46
|
+
|
47
|
+
* Local configuration: (in case you need to access different Dhis2 instances inside a single project):
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
client = Dhis2::Client.new(url: "https://play.dhis2.org/demo", user: "admin", password: "district")
|
51
|
+
client.data_elements.list # => Array[<DataElement>,..]
|
28
52
|
|
29
|
-
|
53
|
+
# Or alternatively
|
54
|
+
client = Dhis2::Client.new("https://admin:district@play.dhis2.org/demo")
|
55
|
+
client.data_elements.list # => Array[<DataElement>,..]
|
56
|
+
```
|
30
57
|
|
31
|
-
|
58
|
+
### Search for meta elements
|
32
59
|
|
33
|
-
|
60
|
+
All subsequent calls can be done on the objects themselves and are going to use the provided url and credentials
|
61
|
+
|
62
|
+
org_unit_levels = Dhis2.client.organisation_unit_levels.list
|
34
63
|
|
35
64
|
The various methods are taking an optional hash parameter to be used for ´filter´ and ´fields´ values:
|
36
65
|
|
37
|
-
org_units = Dhis2
|
66
|
+
org_units = Dhis2.client.organisation_units.list(filter: "level:eq:2", fields: %w(id level displayName parent))
|
38
67
|
|
39
68
|
If you want all fields, simply specify `:all`
|
40
69
|
|
41
|
-
org_units = Dhis2
|
70
|
+
org_units = Dhis2.client.organisation_units.list(filter: "level:eq:2", fields: :all)
|
42
71
|
|
43
72
|
Notes that any field found in the resulting JSON will be accessible from the object.
|
44
73
|
|
@@ -46,7 +75,7 @@ Notes that any field found in the resulting JSON will be accessible from the obj
|
|
46
75
|
|
47
76
|
Following the DHIS2 API, all calls are paginated - you can access the page info using the `pager` property on the returned list:
|
48
77
|
|
49
|
-
org_units = Dhis2
|
78
|
+
org_units = Dhis2.client.organisation_units.list(filter: "level:eq:2", fields: %w(id level displayName parent))
|
50
79
|
org_units.pager.page # current page
|
51
80
|
org_units.pager.page_count # number of pages
|
52
81
|
org_units.pager.total # number of records
|
@@ -55,37 +84,60 @@ Following the DHIS2 API, all calls are paginated - you can access the page info
|
|
55
84
|
|
56
85
|
You can also retreive a single element using its id with `find`(in this case, all fields are returned by default):
|
57
86
|
|
58
|
-
ou = Dhis2.
|
87
|
+
ou = Dhis2.client.organisation_units.find(id)
|
88
|
+
|
89
|
+
`find` also accepts multiple ids - query will not be paginated and will return all fields for the given objects:
|
90
|
+
|
91
|
+
ous = Dhis2.client.organisation_units.find([id1, id2, id3])
|
59
92
|
|
60
93
|
If you have an equality condition or set of equality conditions that should return a single element, you can use `find_by` instead of the longer list option:
|
61
94
|
|
62
95
|
# Instead of this:
|
63
|
-
data_element = Dhis2
|
96
|
+
data_element = Dhis2.client.data_elements.list(filter: "code:eq:C27", fields: :all).first
|
64
97
|
|
65
98
|
# Just do:
|
66
|
-
data_element = Dhis2
|
99
|
+
data_element = Dhis2.client.data_elements.find_by(code: "C27")
|
100
|
+
|
101
|
+
### Values
|
102
|
+
|
103
|
+
You can retreive data values this way:
|
104
|
+
|
105
|
+
ds = Dhis2.client.data_sets.find_by(name: "Child Health")
|
106
|
+
organisation_unit = Dhis2.client.organisation_units.find_by(name: "Baoma")
|
107
|
+
period = "201512"
|
108
|
+
value_sets = Dhis2.client.data_value_sets.list(
|
109
|
+
data_sets: [ds.id],
|
110
|
+
organisation_unit: organisation_unit.id, periods: [period]
|
111
|
+
)
|
67
112
|
|
68
113
|
## Supported features
|
69
114
|
|
70
|
-
The API is currently limited to **read** actions on the following elements:
|
115
|
+
The API is currently limited to **read** actions on the following elements/classes:
|
71
116
|
|
72
|
-
*
|
73
|
-
*
|
74
|
-
*
|
75
|
-
*
|
117
|
+
* `OrganisationUnit`
|
118
|
+
* `OrganisationUnitLevels`
|
119
|
+
* `DataElement`
|
120
|
+
* `DataSet`
|
121
|
+
* `CategoryCombo`
|
76
122
|
|
77
|
-
A very basic **write** use case exists for
|
123
|
+
A very basic **write** use case exists for `DataElement` and `DataSet`:
|
78
124
|
|
79
125
|
elements = [
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
status = Dhis2
|
126
|
+
{ name: "TesTesT1", short_name: "TTT1" },
|
127
|
+
{ name: "TesTesT2", short_name: "TTT2" }
|
128
|
+
]
|
129
|
+
status = Dhis2.client.data_elements.create(elements)
|
84
130
|
status.success? # => true
|
85
131
|
status.total_imported # => 2
|
86
132
|
|
87
133
|
DHIS2 API does not return the ids of the created elements, but you can retreive them with their (unique) name or code.
|
88
134
|
|
135
|
+
elements = [
|
136
|
+
{ name: "TesTesT2", short_name: "TTT2" }
|
137
|
+
]
|
138
|
+
status = Dhis2.client.data_elements.create(elements)
|
139
|
+
element = Dhis2.client.data_elements.find_by(name: "TesTesT2")
|
140
|
+
|
89
141
|
## Development
|
90
142
|
|
91
143
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. Note that the tests are using the DHIS2 demo server, which is reset every day but can be updated by anyone - so if someone change the password of the default user, the tests are going to fail.
|
@@ -101,5 +153,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/BLSQ/d
|
|
101
153
|
## License
|
102
154
|
|
103
155
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
104
|
-
|
105
|
-
z
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class Analytic < Base
|
4
|
+
class << self
|
5
|
+
def list(client, periods:, organisation_units:, data_elements:)
|
6
|
+
params = RestClient::ParamsArray.new([
|
7
|
+
[:dimension, "ou:#{organisation_units}"],
|
8
|
+
[:dimension, "dx:#{data_elements}"],
|
9
|
+
[:dimension, "pe:#{periods}"]
|
10
|
+
])
|
11
|
+
client.get(self.resource_name, params)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class Base < OpenStruct
|
4
|
+
class << self
|
5
|
+
def inherited(base)
|
6
|
+
Dhis2::Client.register_resource(base)
|
7
|
+
end
|
8
|
+
|
9
|
+
def find(client, id, options = {})
|
10
|
+
raise "Missing id" if id.nil?
|
11
|
+
|
12
|
+
if id.class == Array
|
13
|
+
list(client, filter: "id:in:[#{id.join(',')}]", fields: :all, page_size: id.size)
|
14
|
+
elsif options.any?
|
15
|
+
params = []
|
16
|
+
options.each do |name, value|
|
17
|
+
params << [name, value]
|
18
|
+
end
|
19
|
+
params = client.class.deep_change_case(params, :camelize)
|
20
|
+
json_response = client.get("#{resource_name}/#{id}", RestClient::ParamsArray.new(params))
|
21
|
+
new(client, json_response)
|
22
|
+
else
|
23
|
+
response = client.get("#{resource_name}/#{id}")
|
24
|
+
new(client, response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_by(client, clauses)
|
29
|
+
filter = []
|
30
|
+
clauses.each do |field, value|
|
31
|
+
filter << "#{field}:eq:#{value}"
|
32
|
+
end
|
33
|
+
list(client, fields: :all, filter: filter).first
|
34
|
+
end
|
35
|
+
|
36
|
+
def list(client, options = {})
|
37
|
+
json_response = client.get(resource_name, format_query_parameters(options))
|
38
|
+
resource_key = client.class.underscore(resource_name)
|
39
|
+
PaginatedArray.new(
|
40
|
+
json_response[resource_key].map { |raw_resource| new(client, raw_resource) },
|
41
|
+
json_response["pager"]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def resource_name
|
46
|
+
simple_name = name.split("::").last
|
47
|
+
simple_name[0].downcase + simple_name[1..-1] + "s"
|
48
|
+
end
|
49
|
+
|
50
|
+
def format_query_parameters(options)
|
51
|
+
params = []
|
52
|
+
params.push([:page, options[:page]]) if options[:page]
|
53
|
+
params.push([:pageSize, options[:page_size]]) if options[:page_size]
|
54
|
+
params.push([:fields, format_fields(options[:fields])]) if options[:fields]
|
55
|
+
params.concat(format_filter(options[:filter])) if options[:filter]
|
56
|
+
|
57
|
+
RestClient::ParamsArray.new(params)
|
58
|
+
end
|
59
|
+
|
60
|
+
def format_fields(fields)
|
61
|
+
if fields.respond_to?(:join)
|
62
|
+
fields.join(",")
|
63
|
+
elsif fields == :all
|
64
|
+
":all"
|
65
|
+
else
|
66
|
+
fields
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_filter(filter)
|
71
|
+
if filter.respond_to?(:map)
|
72
|
+
filter.map do |subfilter|
|
73
|
+
[:filter, subfilter]
|
74
|
+
end
|
75
|
+
else
|
76
|
+
[[:filter, filter]]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize(client, raw_data)
|
82
|
+
super(raw_data)
|
83
|
+
self.client = client
|
84
|
+
end
|
85
|
+
|
86
|
+
def update_attributes(attributes)
|
87
|
+
client.patch("#{self.class.resource_name}/#{id}", attributes)
|
88
|
+
attributes.each do |key, value|
|
89
|
+
self[key] = value
|
90
|
+
end
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def delete
|
95
|
+
client.delete("#{self.class.resource_name}/#{id}")
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def ==(other)
|
100
|
+
self.class == other.class && id == other.id
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class CategoryCombo < Base
|
4
|
+
class << self
|
5
|
+
def defaut
|
6
|
+
find_by(name: "default")
|
7
|
+
end
|
8
|
+
|
9
|
+
def create(client, combos)
|
10
|
+
combos = [combos].flatten
|
11
|
+
category_combo = {
|
12
|
+
categoryCombos: combos.map do |combo|
|
13
|
+
{
|
14
|
+
name: combo[:name],
|
15
|
+
data_dimension_type: combo[:aggregation_type] || "DISAGGREGATION"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
}
|
19
|
+
|
20
|
+
response = client.post("metadata", category_combo)
|
21
|
+
Dhis2::Status.new(response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class DataElement < Base
|
4
|
+
class << self
|
5
|
+
def create(client, elements)
|
6
|
+
elements = [elements].flatten
|
7
|
+
category_combo_id = client.category_combos.find_by(name: "default").id
|
8
|
+
|
9
|
+
data_element = {
|
10
|
+
data_elements: elements.map do |element|
|
11
|
+
{
|
12
|
+
name: element[:name],
|
13
|
+
short_name: element[:short_name],
|
14
|
+
code: element[:code] || element[:short_name],
|
15
|
+
domain_type: element[:domain_type] || "AGGREGATE",
|
16
|
+
value_type: element[:value_type] || "NUMBER",
|
17
|
+
aggregation_type: element[:aggregation_type] || "SUM",
|
18
|
+
type: element[:type] || "int", # for backward compatbility
|
19
|
+
aggregation_operator: element[:aggregation_type] || "SUM", # for backward compatbility
|
20
|
+
category_combo: { id: category_combo_id }
|
21
|
+
}
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
response = client.post("metadata", data_element)
|
26
|
+
Dhis2::Status.new(response)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class DataElementGroup < Base
|
4
|
+
class << self
|
5
|
+
def create(client, groups)
|
6
|
+
groups = [groups].flatten
|
7
|
+
de_groups = {
|
8
|
+
data_element_groups: groups.map do |group|
|
9
|
+
{
|
10
|
+
name: group[:name],
|
11
|
+
short_name: group[:short_name],
|
12
|
+
code: group[:code] || group[:short_name]
|
13
|
+
}
|
14
|
+
end
|
15
|
+
}
|
16
|
+
response = client.post("metadata", de_groups)
|
17
|
+
Dhis2::Status.new(response)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class DataSet < Base
|
4
|
+
class << self
|
5
|
+
def create(client, sets)
|
6
|
+
sets = [sets].flatten
|
7
|
+
data_set = {
|
8
|
+
data_sets: sets.map do |set|
|
9
|
+
{
|
10
|
+
name: set[:name],
|
11
|
+
short_name: set[:short_name],
|
12
|
+
code: set[:code],
|
13
|
+
period_type: "Monthly",
|
14
|
+
data_elements: set[:data_element_ids] ? set[:data_element_ids].map { |id| { id: id } } : [],
|
15
|
+
organisation_units: set[:organisation_unit_ids] ? set[:organisation_unit_ids].map { |id| { id: id } } : []
|
16
|
+
}
|
17
|
+
end
|
18
|
+
}
|
19
|
+
response = client.post("metadata", data_set)
|
20
|
+
Dhis2::Status.new(response)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class DataValue < Base
|
4
|
+
class << self
|
5
|
+
def find(client, period:, organisation_unit:, data_element:)
|
6
|
+
params = { pe: period, ou: organisation_unit, de: data_element }
|
7
|
+
|
8
|
+
client.get(self.resource_name, params).first
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class DataValueSet < Base
|
4
|
+
def values
|
5
|
+
data_values.map do |data_value|
|
6
|
+
OpenStruct.new(data_value)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def create(client, tuples)
|
12
|
+
body = { dataValues: tuples }
|
13
|
+
response = client.post(self.resource_name, body)
|
14
|
+
Dhis2::Status.new(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
def list(client, options)
|
18
|
+
data_set_ids = options[:data_sets]
|
19
|
+
periods = options[:periods]
|
20
|
+
|
21
|
+
organisation_unit_id = options[:organisation_unit]
|
22
|
+
children = options[:children] || true
|
23
|
+
|
24
|
+
if organisation_unit_id.class == Array
|
25
|
+
ou_url = organisation_unit_id.map { |ou_id| "orgUnit=#{ou_id}" }.join("&") + "&children=#{children}"
|
26
|
+
else
|
27
|
+
ou_url = "orgUnit=#{organisation_unit_id}&children=#{children}"
|
28
|
+
end
|
29
|
+
|
30
|
+
data_sets_url = data_set_ids.map { |ds| "dataSet=#{ds}" }.join("&")
|
31
|
+
periods = periods.map { |period| "period=#{period}" }.join("&")
|
32
|
+
|
33
|
+
params = [data_sets_url, periods, ou_url].join("&")
|
34
|
+
new(client, client.get(self.resource_name + "?" + params))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Dhis2
|
2
|
+
module Api
|
3
|
+
class OrganisationUnit < Base
|
4
|
+
def initialize(client, params)
|
5
|
+
super
|
6
|
+
self.parent_id = params["parent"]["id"] if params["parent"]
|
7
|
+
self.children_ids = params["children"] ? params["children"].map { |raw| raw["id"] } : []
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def find(client, id, options = {})
|
12
|
+
raise "Missing id" if id.nil?
|
13
|
+
if id.class == Array
|
14
|
+
list(client, filter: "id:in:[#{id.join(',')}]", fields: :all, page_size: id.size)
|
15
|
+
elsif options.any?
|
16
|
+
params = []
|
17
|
+
options.each do |name, value|
|
18
|
+
params << [name, value]
|
19
|
+
end
|
20
|
+
params = client.class.deep_change_case(params, :camelize)
|
21
|
+
json_response = client.get("#{resource_name}/#{id}", RestClient::ParamsArray.new(params))
|
22
|
+
if options[:include_descendants] || options[:include_children]
|
23
|
+
json_response = client.get(resource_name, format_query_parameters(options))
|
24
|
+
resource_key = client.class.underscore(resource_name)
|
25
|
+
PaginatedArray.new(
|
26
|
+
json_response[resource_key].map { |raw_resource| new(client, raw_resource) },
|
27
|
+
json_response["pager"]
|
28
|
+
)
|
29
|
+
else
|
30
|
+
new(client, json_response)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
response = client.get("#{resource_name}/#{id}")
|
34
|
+
new(client, response)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def create(client, orgunits)
|
39
|
+
orgunits = [orgunits].flatten
|
40
|
+
|
41
|
+
payload = {
|
42
|
+
organisationUnits: orgunits.map do |orgunit|
|
43
|
+
organisation_unit = {
|
44
|
+
name: orgunit[:name],
|
45
|
+
short_name: orgunit[:short_name],
|
46
|
+
opening_date: orgunit[:opening_date]
|
47
|
+
}
|
48
|
+
organisation_unit[:parent] = {id: orgunit[:parent_id]} if orgunit[:parent_id]
|
49
|
+
organisation_unit
|
50
|
+
end
|
51
|
+
}
|
52
|
+
|
53
|
+
response = client.post("metadata", payload)
|
54
|
+
Dhis2::Status.new(response)
|
55
|
+
end
|
56
|
+
|
57
|
+
def last_level_descendants(client, id)
|
58
|
+
levels = client.organisation_unit_levels.list(fields: :all)
|
59
|
+
last_level = levels.map(&:level).sort.last
|
60
|
+
|
61
|
+
client.organisation_units.find(id, include_descendants: true).select do |ou|
|
62
|
+
ou.level == last_level
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/dhis2/client.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module Dhis2
|
2
|
+
class Client
|
3
|
+
def self.register_resource(resource_class)
|
4
|
+
class_name = resource_class.name.split("::").last
|
5
|
+
method_name = underscore(class_name) + "s"
|
6
|
+
define_method(method_name) do
|
7
|
+
CollectionWrapper.new(resource_class, self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.deep_change_case(hash, type)
|
12
|
+
case hash
|
13
|
+
when Array
|
14
|
+
hash.map {|v| deep_change_case(v, type) }
|
15
|
+
when Hash
|
16
|
+
new_hash = {}
|
17
|
+
hash.each do |k, v|
|
18
|
+
new_key = type == :underscore ? underscore(k.to_s) : camelize(k.to_s, false)
|
19
|
+
new_hash[new_key] = deep_change_case(v, type)
|
20
|
+
end
|
21
|
+
new_hash
|
22
|
+
else
|
23
|
+
hash
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.camelize(string, uppercase_first_letter = true)
|
28
|
+
if uppercase_first_letter
|
29
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
30
|
+
else
|
31
|
+
string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
|
32
|
+
end
|
33
|
+
string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.underscore(camel_cased_word)
|
37
|
+
return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
|
38
|
+
word = camel_cased_word.to_s.gsub(/::/, '/')
|
39
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
40
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
41
|
+
word.tr!("-", "_")
|
42
|
+
word.downcase!
|
43
|
+
word
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(options)
|
47
|
+
if options.is_a?(String)
|
48
|
+
@base_url = options
|
49
|
+
else
|
50
|
+
raise "Missing :url attribute" unless options[:url]
|
51
|
+
raise "Missing :user attribute" unless options[:user]
|
52
|
+
raise "Missing :password attribute" unless options[:password]
|
53
|
+
url = URI.parse(options[:url])
|
54
|
+
url.user = CGI.escape(options[:user])
|
55
|
+
url.password = CGI.escape(options[:password])
|
56
|
+
@base_url = url.to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def post(path, payload, query_params = {})
|
61
|
+
execute(:post, uri(path), headers, query_params, payload)
|
62
|
+
end
|
63
|
+
|
64
|
+
def get(path, query_params = {})
|
65
|
+
execute(:get, uri(path), headers, query_params)
|
66
|
+
end
|
67
|
+
|
68
|
+
def delete(path, query_params = {})
|
69
|
+
execute(:delete, uri(path), headers, query_params)
|
70
|
+
end
|
71
|
+
|
72
|
+
def put(path, payload, query_params = {})
|
73
|
+
execute(:put, uri(path), headers, query_params, payload)
|
74
|
+
end
|
75
|
+
|
76
|
+
def patch(path, payload, query_params = {})
|
77
|
+
execute(:patch, uri(path), headers, query_params, payload)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def execute(method, url, headers, query_params = {}, payload = nil)
|
83
|
+
query = {
|
84
|
+
method: method,
|
85
|
+
url: url,
|
86
|
+
headers: { params: query_params }.merge(headers),
|
87
|
+
payload: payload ? self.class.deep_change_case(payload, :camelize).to_json : nil
|
88
|
+
}
|
89
|
+
|
90
|
+
raw_response = RestClient::Request.execute(query)
|
91
|
+
response = raw_response.nil? || raw_response == "" ? {} : JSON.parse(raw_response)
|
92
|
+
response = self.class.deep_change_case(response, :underscore)
|
93
|
+
|
94
|
+
if response.class == Hash && response["import_type_summaries"] &&
|
95
|
+
response["import_type_summaries"][0] &&
|
96
|
+
response["import_type_summaries"][0]["import_conflicts"] &&
|
97
|
+
!response["import_type_summaries"].first["import_conflicts"].empty?
|
98
|
+
raise Dhis2::ImportError, response["import_type_summaries"].first["import_conflicts"].first["value"].inspect
|
99
|
+
end
|
100
|
+
response
|
101
|
+
end
|
102
|
+
|
103
|
+
def uri(path)
|
104
|
+
File.join(@base_url, "api", path)
|
105
|
+
end
|
106
|
+
|
107
|
+
def headers
|
108
|
+
{
|
109
|
+
content_type: :json,
|
110
|
+
accept: :json
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Dhis2
|
2
|
+
class CollectionWrapper
|
3
|
+
def initialize(klass, client)
|
4
|
+
@klass = klass
|
5
|
+
@client = client
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(method_name, *args, &block)
|
9
|
+
args = args.unshift(@client)
|
10
|
+
@klass.__send__(method_name, *args, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/dhis2/pager.rb
ADDED
data/lib/dhis2/status.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Dhis2
|
2
|
+
class Status
|
3
|
+
attr_reader :raw_status
|
4
|
+
|
5
|
+
def initialize(raw_status)
|
6
|
+
@raw_status = raw_status
|
7
|
+
end
|
8
|
+
|
9
|
+
def success?
|
10
|
+
if @raw_status["import_type_summaries"]
|
11
|
+
return @raw_status["import_type_summaries"].all? do |summary|
|
12
|
+
summary["status"] == "SUCCESS"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@raw_status["status"] == "SUCCESS"
|
16
|
+
end
|
17
|
+
|
18
|
+
def import_count
|
19
|
+
@raw_status["import_count"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def total_imported
|
23
|
+
total = 0
|
24
|
+
import_count.each do |_, count|
|
25
|
+
total += count
|
26
|
+
end
|
27
|
+
total
|
28
|
+
end
|
29
|
+
|
30
|
+
def last_imported_ids
|
31
|
+
@raw_status["import_type_summaries"].map { |summary| summary["last_imported"] }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/dhis2/version.rb
CHANGED
data/lib/dhis2.rb
CHANGED
@@ -1,47 +1,56 @@
|
|
1
|
-
require "dhis2/version"
|
2
1
|
require "rest-client"
|
3
2
|
require "json"
|
4
|
-
require "
|
5
|
-
require "
|
6
|
-
require "
|
7
|
-
require "
|
8
|
-
require "status"
|
9
|
-
require "paginated_array"
|
3
|
+
require "ostruct"
|
4
|
+
require "uri"
|
5
|
+
require "delegate"
|
6
|
+
require "cgi"
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
require_relative "dhis2/version"
|
9
|
+
require_relative "dhis2/configuration"
|
10
|
+
require_relative "dhis2/collection_wrapper"
|
11
|
+
require_relative "dhis2/pager"
|
12
|
+
require_relative "dhis2/paginated_array"
|
13
|
+
require_relative "dhis2/import_error"
|
14
|
+
require_relative "dhis2/status"
|
15
|
+
require_relative "dhis2/client"
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
require_relative "dhis2/api/base"
|
18
|
+
require_relative "dhis2/api/category_combo"
|
19
|
+
require_relative "dhis2/api/organisation_unit"
|
20
|
+
require_relative "dhis2/api/data_element"
|
21
|
+
require_relative "dhis2/api/data_element_group"
|
22
|
+
require_relative "dhis2/api/data_set"
|
23
|
+
require_relative "dhis2/api/data_value_set"
|
24
|
+
require_relative "dhis2/api/data_value"
|
25
|
+
require_relative "dhis2/api/organisation_unit_level"
|
26
|
+
require_relative "dhis2/api/indicator"
|
27
|
+
require_relative "dhis2/api/analytic"
|
28
|
+
require_relative "dhis2/api/organisation_unit_group"
|
24
29
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
module Dhis2
|
31
|
+
class << self
|
32
|
+
def client
|
33
|
+
if @client.nil?
|
34
|
+
if config.user.nil? && config.password.nil?
|
35
|
+
@client ||= Dhis2::Client.new(config.url)
|
36
|
+
else
|
37
|
+
@client ||= Dhis2::Client.new({
|
38
|
+
url: config.url,
|
39
|
+
user: config.user,
|
40
|
+
password: config.password
|
41
|
+
})
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@client
|
30
45
|
end
|
31
|
-
arguments << "filter=" + options[:filter] if options[:filter]
|
32
|
-
arguments << "pageSize=#{options[:page_size]}" if options[:page_size]
|
33
|
-
arguments << "page=#{options[:page]}" if options[:page]
|
34
|
-
|
35
|
-
path = "#{name}?#{arguments.join('&')}"
|
36
|
-
resource[path]
|
37
46
|
end
|
38
47
|
|
39
|
-
def
|
40
|
-
|
48
|
+
def configure(&block)
|
49
|
+
yield config
|
41
50
|
end
|
42
51
|
|
43
|
-
def
|
44
|
-
|
52
|
+
def config
|
53
|
+
@configuration ||= Dhis2::Configuration.new
|
45
54
|
end
|
46
55
|
end
|
47
56
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dhis2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Van Aken
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -85,16 +85,27 @@ files:
|
|
85
85
|
- bin/console
|
86
86
|
- bin/setup
|
87
87
|
- dhis2.gemspec
|
88
|
-
- lib/base.rb
|
89
|
-
- lib/data_element.rb
|
90
|
-
- lib/data_set.rb
|
91
88
|
- lib/dhis2.rb
|
89
|
+
- lib/dhis2/api/analytic.rb
|
90
|
+
- lib/dhis2/api/base.rb
|
91
|
+
- lib/dhis2/api/category_combo.rb
|
92
|
+
- lib/dhis2/api/data_element.rb
|
93
|
+
- lib/dhis2/api/data_element_group.rb
|
94
|
+
- lib/dhis2/api/data_set.rb
|
95
|
+
- lib/dhis2/api/data_value.rb
|
96
|
+
- lib/dhis2/api/data_value_set.rb
|
97
|
+
- lib/dhis2/api/indicator.rb
|
98
|
+
- lib/dhis2/api/organisation_unit.rb
|
99
|
+
- lib/dhis2/api/organisation_unit_group.rb
|
100
|
+
- lib/dhis2/api/organisation_unit_level.rb
|
101
|
+
- lib/dhis2/client.rb
|
102
|
+
- lib/dhis2/collection_wrapper.rb
|
103
|
+
- lib/dhis2/configuration.rb
|
104
|
+
- lib/dhis2/import_error.rb
|
105
|
+
- lib/dhis2/pager.rb
|
106
|
+
- lib/dhis2/paginated_array.rb
|
107
|
+
- lib/dhis2/status.rb
|
92
108
|
- lib/dhis2/version.rb
|
93
|
-
- lib/organisation_unit.rb
|
94
|
-
- lib/organisation_unit_level.rb
|
95
|
-
- lib/pager.rb
|
96
|
-
- lib/paginated_array.rb
|
97
|
-
- lib/status.rb
|
98
109
|
homepage: http://github.com/blsq/dhis2
|
99
110
|
licenses:
|
100
111
|
- MIT
|
data/lib/base.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
module Dhis2
|
2
|
-
class Base
|
3
|
-
def initialize(raw_data)
|
4
|
-
@raw_data = raw_data
|
5
|
-
@id = raw_data["id"]
|
6
|
-
@display_name = raw_data["displayName"]
|
7
|
-
end
|
8
|
-
|
9
|
-
def method_missing(m, *args, &block)
|
10
|
-
return @raw_data[m.to_s] if @raw_data[m.to_s]
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
class << self
|
15
|
-
def find(id)
|
16
|
-
response = Dhis2.get_resource("#{resource_name}/#{id}").get
|
17
|
-
json_response = JSON.parse(response)
|
18
|
-
new(json_response)
|
19
|
-
end
|
20
|
-
|
21
|
-
def find_by(clauses)
|
22
|
-
filter = []
|
23
|
-
clauses.each do |field, value|
|
24
|
-
filter << "#{field}:eq:#{value}"
|
25
|
-
end
|
26
|
-
list(fields: :all, filter: filter.join("&")).first
|
27
|
-
end
|
28
|
-
|
29
|
-
def list(options = {})
|
30
|
-
options[:fields] = default_fields if default_fields && !options[:fields]
|
31
|
-
response = Dhis2.get_resource(resource_name, options).get
|
32
|
-
json_response = JSON.parse(response)
|
33
|
-
PaginatedArray.new(
|
34
|
-
json_response[resource_name].map { |raw_org_unit| new(raw_org_unit) },
|
35
|
-
json_response["pager"]
|
36
|
-
)
|
37
|
-
end
|
38
|
-
|
39
|
-
def resource_name
|
40
|
-
simple_name = name.split("::").last
|
41
|
-
simple_name[0].downcase + simple_name[1..-1] + "s"
|
42
|
-
end
|
43
|
-
|
44
|
-
def default_fields
|
45
|
-
nil
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/lib/data_element.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require "base"
|
2
|
-
|
3
|
-
module Dhis2
|
4
|
-
class DataElement < Base
|
5
|
-
attr_reader :id, :display_name
|
6
|
-
|
7
|
-
def initialize(params)
|
8
|
-
super(params)
|
9
|
-
end
|
10
|
-
|
11
|
-
class << self
|
12
|
-
def create(elements)
|
13
|
-
elements = [elements].flatten
|
14
|
-
category_combo_id = JSON.parse(Dhis2.resource["categoryCombos"].get)["categoryCombos"]
|
15
|
-
.first["id"]
|
16
|
-
data_element = {
|
17
|
-
dataElements: elements.map do |element|
|
18
|
-
{
|
19
|
-
name: element[:name],
|
20
|
-
shortName: element[:short_name],
|
21
|
-
code: element[:code] || element[:short_name],
|
22
|
-
domainType: element[:domain_type] || "AGGREGATE",
|
23
|
-
valueType: element[:value_type] || "INTEGER_POSITIVE",
|
24
|
-
aggregationType: element[:aggregation_type] || "SUM",
|
25
|
-
categoryCombo: { id: category_combo_id }
|
26
|
-
}
|
27
|
-
end
|
28
|
-
}
|
29
|
-
json_response = Dhis2.resource["metadata"].post(
|
30
|
-
JSON.generate(data_element),
|
31
|
-
content_type: "application/json"
|
32
|
-
)
|
33
|
-
response = JSON.parse(json_response)
|
34
|
-
|
35
|
-
Dhis2::Status.new(response)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/lib/data_set.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require "base"
|
2
|
-
|
3
|
-
module Dhis2
|
4
|
-
class DataSet < Base
|
5
|
-
def initialize(params)
|
6
|
-
super(params)
|
7
|
-
end
|
8
|
-
|
9
|
-
class << self
|
10
|
-
def create(sets)
|
11
|
-
sets = [sets].flatten
|
12
|
-
|
13
|
-
data_set = {
|
14
|
-
dataSets: sets.map do |set|
|
15
|
-
{
|
16
|
-
name: set[:name],
|
17
|
-
shortName: set[:short_name],
|
18
|
-
code: set[:code],
|
19
|
-
periodType: "Monthly",
|
20
|
-
dataElements: set[:data_element_ids] ? set[:data_element_ids].map { |id| { id: id } } : [],
|
21
|
-
organisationUnits: set[:organisation_unit_ids] ? set[:organisation_unit_ids].map { |id| { id: id } } : []
|
22
|
-
}
|
23
|
-
end
|
24
|
-
}
|
25
|
-
json_response = Dhis2.resource["metadata"].post(
|
26
|
-
JSON.generate(data_set),
|
27
|
-
content_type: "application/json"
|
28
|
-
)
|
29
|
-
response = JSON.parse(json_response)
|
30
|
-
|
31
|
-
Dhis2::Status.new(response)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
data/lib/organisation_unit.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require "base"
|
2
|
-
|
3
|
-
module Dhis2
|
4
|
-
class OrganisationUnit < Base
|
5
|
-
attr_reader :id, :display_name, :level, :parent_id, :children_ids
|
6
|
-
|
7
|
-
def initialize(params)
|
8
|
-
super(params)
|
9
|
-
@level = params["level"]
|
10
|
-
@parent_id = params["parent"]["id"] if params["parent"]
|
11
|
-
@children_ids = params["children"] ? params["children"].map { |raw| raw["id"] } : []
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require "base"
|
2
|
-
|
3
|
-
module Dhis2
|
4
|
-
class OrganisationUnitLevel < Base
|
5
|
-
attr_reader :id, :name, :level
|
6
|
-
|
7
|
-
def initialize(params)
|
8
|
-
super(params)
|
9
|
-
@name = params["name"]
|
10
|
-
@level = params["level"]
|
11
|
-
end
|
12
|
-
|
13
|
-
class << self
|
14
|
-
def default_fields
|
15
|
-
%w(id name level)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/lib/pager.rb
DELETED
data/lib/paginated_array.rb
DELETED
data/lib/status.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
module Dhis2
|
2
|
-
class Status
|
3
|
-
attr_reader :raw_status
|
4
|
-
|
5
|
-
def initialize(raw_status)
|
6
|
-
@raw_status = raw_status
|
7
|
-
end
|
8
|
-
|
9
|
-
def success?
|
10
|
-
@raw_status["importTypeSummaries"].all? { |summary| summary["status"] == "SUCCESS" }
|
11
|
-
end
|
12
|
-
|
13
|
-
def import_count
|
14
|
-
@raw_status["importCount"]
|
15
|
-
end
|
16
|
-
|
17
|
-
def total_imported
|
18
|
-
total = 0
|
19
|
-
import_count.each do |_, count|
|
20
|
-
total += count
|
21
|
-
end
|
22
|
-
total
|
23
|
-
end
|
24
|
-
|
25
|
-
def last_imported_ids
|
26
|
-
@raw_status["importTypeSummaries"].map { |summary| summary["lastImported"] }
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# {
|
32
|
-
# "importCount"=>{
|
33
|
-
# "imported"=>1,
|
34
|
-
# "updated"=>0,
|
35
|
-
# "ignored"=>0,
|
36
|
-
# "deleted"=>0
|
37
|
-
# },
|
38
|
-
# "importTypeSummaries"=>[
|
39
|
-
# {
|
40
|
-
# "responseType"=>"ImportTypeSummary",
|
41
|
-
# "status"=>"SUCCESS",
|
42
|
-
# "importCount"=>
|
43
|
-
# {
|
44
|
-
# "imported"=>1,
|
45
|
-
# "updated"=>0,
|
46
|
-
# "ignored"=>0,
|
47
|
-
# "deleted"=>0
|
48
|
-
# },
|
49
|
-
# "type"=>"DataElement",
|
50
|
-
# "lastImported"=>"lMadHjxdo5q"
|
51
|
-
# }
|
52
|
-
# ]
|
53
|
-
# }
|