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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 77af2d4ee9db850966b8fe67375be8554acfbe72
4
- data.tar.gz: 79a9fccf546c3e1b5d94e85ca17718dcfb520c44
3
+ metadata.gz: ed649a064de87ceea944479aa82f20e239e912ee
4
+ data.tar.gz: 862e2be603b90d7da556faae38ba8df02ee51759
5
5
  SHA512:
6
- metadata.gz: 91d423b15b3adcd7371f281b908170ac12a902a408957e18c3b78be32973a3435e1d2479d1c34bd1f0273aea785ece0b368ce183d37291f70529c109b5760065
7
- data.tar.gz: 5dafa945e708eea647ec1b097f10c32b6b83d886bab1f33b9f1741ec0f8719490a81f291e0807fbdbfebd066caad95361c1890ba89b1b534efd5ce87471af636
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', :github => 'BLSQ/dhis2' # not published yet, so get it from github
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
- Dhis2.connect(url: "https://play.dhis2.org/demo", user: "admin", password: "district")
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
- ### Search for elements
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
- All subsequent calls can be done on the object themselves and are going to use the provided url and credentials
58
+ ### Search for meta elements
32
59
 
33
- org_unit_levels = Dhis2::OrganisationUnitLevel.list
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::OrganisationUnit.list(filter: "level:eq:2", fields: %w(id level displayName parent))
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::OrganisationUnit.list(filter: "level:eq:2", fields: :all)
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::OrganisationUnit.list(filter: "level:eq:2", fields: %w(id level displayName parent))
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.Dhis2::OrganisationUnit.find(id)
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::DataElement.list(filter: "code:eq:C27", fields: :all).first
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::DataElement.find_by(code: "C27")
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
- * Organisation Units
73
- * Organisation Units Levels
74
- * Data Elements
75
- * Data Sets
117
+ * `OrganisationUnit`
118
+ * `OrganisationUnitLevels`
119
+ * `DataElement`
120
+ * `DataSet`
121
+ * `CategoryCombo`
76
122
 
77
- A very basic **write** use case exists for Data Elements and Data Sets:
123
+ A very basic **write** use case exists for `DataElement` and `DataSet`:
78
124
 
79
125
  elements = [
80
- { name: "TesTesT1", short_name: "TTT1" },
81
- { name: "TesTesT2", short_name: "TTT2" }
82
- ]
83
- status = Dhis2:DataElement.create(elements)
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,6 @@
1
+ module Dhis2
2
+ module Api
3
+ class Indicator < Base
4
+ end
5
+ end
6
+ 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
@@ -0,0 +1,11 @@
1
+ module Dhis2
2
+ module Api
3
+ class OrganisationUnitGroup < Base
4
+ def organisation_unit_ids
5
+ organisation_units.map do |organisation_unit|
6
+ organisation_unit["id"]
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module Dhis2
2
+ module Api
3
+ class OrganisationUnitLevel < Base
4
+ end
5
+ end
6
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ module Dhis2
2
+ class Configuration
3
+ attr_accessor :url, :user, :password
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Dhis2
2
+ class ImportError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ class Pager
2
+ attr_reader :page, :page_count, :total, :next_page
3
+
4
+ def initialize(hash)
5
+ @page = hash["page"]
6
+ @page_count = hash["page_count"]
7
+ @total = hash["total"]
8
+ @next_page = hash["next_page"]
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Dhis2
2
+ class PaginatedArray < DelegateClass(Array)
3
+ attr_reader :pager
4
+
5
+ def initialize(array, raw_pager = nil)
6
+ super(array)
7
+ @pager = Pager.new(raw_pager) if raw_pager
8
+ end
9
+ end
10
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Dhis2
2
- VERSION = "0.1.0"
2
+ VERSION = "2.1.0"
3
3
  end
data/lib/dhis2.rb CHANGED
@@ -1,47 +1,56 @@
1
- require "dhis2/version"
2
1
  require "rest-client"
3
2
  require "json"
4
- require "organisation_unit"
5
- require "data_element"
6
- require "data_set"
7
- require "organisation_unit_level"
8
- require "status"
9
- require "paginated_array"
3
+ require "ostruct"
4
+ require "uri"
5
+ require "delegate"
6
+ require "cgi"
10
7
 
11
- module Dhis2
12
- class << self
13
- attr_reader :url
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
- def connect(options)
16
- raise "Missing #{url}" unless options[:url]
17
- raise "Missing #{user}" unless options[:user]
18
- raise "Missing #{password}" unless options[:password]
19
-
20
- @url = options[:url]
21
- @user = options[:user]
22
- @password = options[:password]
23
- end
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
- def get_resource(name, options = {})
26
- arguments = []
27
- if options[:fields]
28
- arguments << "fields=" + options[:fields].join(",") if options[:fields].respond_to?(:join)
29
- arguments << "fields=:#{options[:fields]}" if options[:fields].class == Symbol
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 resource
40
- @resource ||= RestClient::Resource.new("#{@url}/api", headers: { accept: "application/json" }, user: @user, password: @password)
48
+ def configure(&block)
49
+ yield config
41
50
  end
42
51
 
43
- def camelize(str)
44
- str.gsub(/\_([a-z])/, "")
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: 0.1.0
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-08-29 00:00:00.000000000 Z
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
@@ -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
@@ -1,10 +0,0 @@
1
- class Pager
2
- attr_reader :page, :page_count, :total, :next_page
3
-
4
- def initialize(hash)
5
- @page = hash["page"]
6
- @page_count = hash["pageCount"]
7
- @total = hash["total"]
8
- @next_page = hash["nextPage"]
9
- end
10
- end
@@ -1,11 +0,0 @@
1
- require "delegate"
2
- require "pager"
3
-
4
- class PaginatedArray < DelegateClass(Array)
5
- attr_reader :pager
6
-
7
- def initialize(array, raw_pager = nil)
8
- super(array)
9
- @pager = Pager.new(raw_pager) if raw_pager
10
- end
11
- end
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
- # }