nesstar_rest_api_client 0.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.
data/LICENSE ADDED
File without changes
@@ -0,0 +1,28 @@
1
+ # Nesstar API for Ruby
2
+
3
+ This is Nesstar REST API client written in Ruby. It represents the REST endpoints as plain old Ruby objects.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ $ rake install
9
+ ```
10
+
11
+ ## Getting started
12
+
13
+ Connect to a server like this:
14
+
15
+ ```
16
+ server = NesstarAPI.get_server "http://my-instance-of-nesstar-rest-api.com"
17
+ ```
18
+
19
+ From a server object you can list studies or load individual studies by ID:
20
+
21
+ ```
22
+ studies = server.get_studies
23
+ my_study = server.get_study "my-study-id"
24
+ ```
25
+
26
+ ## Documentation
27
+
28
+ Run `rake docs` to build the documentation. The documentation will appear in the `docs` folder.
@@ -0,0 +1,14 @@
1
+ require 'nesstar-api/rest-client.rb'
2
+ require 'nesstar-api/server.rb'
3
+
4
+ ##
5
+ # This module is the starting point. Use this to get an instance of a server.
6
+ module NesstarAPI
7
+
8
+ ##
9
+ # Gets a server identified by a URL
10
+ def self.get_server(url)
11
+ RestClient.instance.init url
12
+ Server.new
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ require 'nesstar-api/nesstar-object.rb'
2
+
3
+ ##
4
+ # A category in a variable.
5
+ #
6
+ # Categories have a name, a label, and a frequency
7
+ class Category < NesstarObject
8
+ attr_reader :name, :label, :frequency
9
+
10
+ def initialize(data)
11
+ @label = data['label']
12
+ @value = data['value']
13
+ @frequency = data['frequency']
14
+ @missing = data['missing'] == 'true'
15
+ end
16
+
17
+ ##
18
+ # Returns true if this category is missing
19
+ def missing?
20
+ @missing
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ require 'nesstar-api/rest-client.rb'
2
+
3
+ ##
4
+ # A parent class for all Nesstar objects.
5
+ class NesstarObject
6
+
7
+ private
8
+
9
+ def load_metadata
10
+ path = "#{self.class.name.downcase}/#{@id}"
11
+ @@data ||= get_values path
12
+ end
13
+
14
+ def metadata
15
+ load_metadata
16
+ end
17
+
18
+ def get_values(path)
19
+ RestClient.instance.get_values path
20
+ end
21
+
22
+ def get_binary(path, &block)
23
+ RestClient.instance.get_binary(path, &block)
24
+ end
25
+
26
+ def dig(hash, *path)
27
+ if hash.nil?
28
+ nil
29
+ elsif path.length == 1
30
+ hash[path.pop.to_s]
31
+ elsif hash.respond_to?(:keys)
32
+ key = path.shift.to_s
33
+ dig(hash[key], *path)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,57 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'singleton'
5
+
6
+ class RestClient
7
+ include Singleton
8
+
9
+ def init(url)
10
+ unless url =~ /\//
11
+ url = url + '/'
12
+ end
13
+ @@url = url
14
+ end
15
+
16
+ def get_values(path)
17
+ uri = get_uri(path)
18
+ response = Net::HTTP.get_response uri
19
+ if response.is_a? Net::HTTPServerError
20
+ raise ServerError, parse_server_error(response.body)
21
+ end
22
+ json = JSON.parse response.body
23
+
24
+ json['payload']
25
+ end
26
+
27
+ def get_binary(path)
28
+ uri = get_uri(path)
29
+ Net::HTTP.start(uri.host, uri.port) do | http |
30
+ response = http.get(uri.request_uri)
31
+ if response.is_a? Net::HTTPServerError
32
+ raise ServerError, parse_server_error(response.body)
33
+ end
34
+ yield(response.body)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def get_uri(path)
41
+ url = @@url + path
42
+ URI.parse url
43
+ end
44
+
45
+ def parse_server_error(error)
46
+ begin
47
+ json = JSON.parse(error)
48
+ error = json['error']
49
+ "#{error['type']}: #{error['message']}"
50
+ rescue JSON::ParserError
51
+ "Fatal: Unknown server error occurred"
52
+ end
53
+ end
54
+
55
+ class ServerError < StandardError
56
+ end
57
+ end
@@ -0,0 +1,30 @@
1
+ require 'nesstar-api/nesstar-object.rb'
2
+ require 'nesstar-api/study.rb'
3
+
4
+ ##
5
+ # Represents the server.
6
+ class Server < NesstarObject
7
+
8
+ ##
9
+ # Get a list of studies on the server.
10
+ def get_studies
11
+ json_studies = get_values "studies"
12
+ json_studies.collect do | study |
13
+ Study.new study
14
+ end
15
+ end
16
+
17
+ ##
18
+ # Get a particular study identified by its ID.
19
+ def get_study(id)
20
+ json = get_values "study/#{id}"
21
+ if json
22
+ study_id = dig(json, :ID)
23
+ name = dig(json, :stdyDscr, :citation, :titlStmt, :titl)
24
+ abstract = dig(json, :stdyDscr, :stdyInfo, :abstract)
25
+
26
+ Study.new({'id' => study_id, 'name' => name, 'abstract' => abstract})
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,118 @@
1
+ require 'tempfile'
2
+ require 'nesstar-api/nesstar-object'
3
+ require 'nesstar-api/variable'
4
+ require 'nesstar-api/variable-group'
5
+ require 'nesstar-api/variable-container'
6
+ require 'nesstar-api/tabulation'
7
+
8
+ ## Represents a study
9
+ class Study < NesstarObject
10
+ attr_reader :id, :name, :abstract
11
+
12
+ include VariableContainer
13
+
14
+ NSDSTAT = "NSDSTAT"
15
+ SAS = "SAS"
16
+ SPSS = "SPSS"
17
+ SPSSPORT = "SPSSPORT"
18
+ STATA = "STATA"
19
+ STATA6 = "STATA6"
20
+ STATA7 = "STATA7"
21
+
22
+ def initialize(data)
23
+ @id = data['id']
24
+ @name = data['name']
25
+ @abstract = data['abstract']
26
+ @last_update = data['updatedTimeStamp']
27
+ end
28
+
29
+ ##
30
+ # Returns a cross tabulation on this study
31
+ #
32
+ # The options parameter holds the following data:
33
+ #
34
+ # * +breakVars+ - A list of variable objects
35
+ # * +msrVar+ - The measure variable, if any
36
+ # * +msrType+ - A list of measure types to use. Valid types are MEAN, MEDIAN, STDDEV, MIN, MAX, SUM, COUNT, CI95MIN, CI95MAX, CI99MIN, CI99MAX, Q1, Q3, WHISKER_LO, WHISKER_HI, SUM_SQUARES, SE_MEAN
37
+ # * +casesubset+ - A subset expression to limit the categories to include. Documentation on expressions is found here: http://nesstar-dev.nsd.uib.no/javadoc/com.nesstar/nesstar-api/0.6.5/com/nesstar/api/subset/CaseSubset.html#compile
38
+ def tabulate(options)
39
+ path = "study/#{@id}/tabulate/"
40
+ query_string = prepare_query_string_for_tabulation options
41
+
42
+ if query_string.length > 0
43
+ path += "?" + query_string.join('&')
44
+ end
45
+
46
+ tabulation = get_values path
47
+ Tabulation.new tabulation, options
48
+ end
49
+
50
+ def correlate
51
+ end
52
+
53
+ def regress
54
+ end
55
+
56
+ ##
57
+ # Downloads the study (or parts of it) in different file formats.
58
+ #
59
+ # The file will be a zip file containing one or more files depending on the requested format.
60
+ #
61
+ # The +format+ parameter should be one of the constants in this class.
62
+ #
63
+ # To only download some variables, supply a list of the variables you wish.
64
+ #
65
+ # The download can be further refined using a case subset. See http://nesstar-dev.nsd.uib.no/javadoc/com.nesstar/nesstar-api/0.6.5/com/nesstar/api/subset/CaseSubset.html#compile for more information.
66
+ def download(format, variables = [], case_subset = nil)
67
+ path = "study/#{@id}/download"
68
+
69
+ query_string = ["format=" + format]
70
+ query_string += collect_list(variables, "var", :id)
71
+ query_string.push("caseSubset=" + case_subset) unless case_subset.nil?
72
+
73
+ path += "?" + query_string.join('&')
74
+
75
+ get_binary(path) do | data |
76
+ if block_given?
77
+ yield(data)
78
+ else
79
+ file = Tempfile.new("#{@name}.#{format}")
80
+ file.write(data)
81
+ file.close
82
+ return file
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def get_variable_group_type
90
+ 'variable-groups'
91
+ end
92
+
93
+ def get_rest_type
94
+ 'study'
95
+ end
96
+
97
+ def prepare_query_string_for_tabulation(options)
98
+ query_string = []
99
+
100
+ query_string += collect_list(options['breakVars'], 'breakVar', :id)
101
+ query_string += collect_list(options['msrType'], 'msrType')
102
+ query_string.push "msrVar=#{options['msrVar'].id}" unless options['mrsVar'].nil?
103
+ query_string.push(options['casesubset']) unless options['casesubset'].nil?
104
+
105
+ query_string
106
+ end
107
+
108
+ def collect_list(list, name, attribute = nil)
109
+ if list.nil?
110
+ []
111
+ else
112
+ list.collect { | obj |
113
+ value = attribute.nil? ? obj : obj.send(attribute)
114
+ "#{name}=#{value}"
115
+ }
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,14 @@
1
+ ##
2
+ # Represents a tabulation run on a +Study+.
3
+ class Tabulation
4
+ attr_reader :data, :variables, :measure, :measure_types, :case_subset
5
+
6
+ def initialize(data, input)
7
+ @data = data['data']
8
+ @variables = input['breakVars']
9
+ @measure = input['msrVar']
10
+ @measure_types = input['msrType']
11
+ @case_subset = input['casesubset']
12
+ end
13
+
14
+ end
@@ -0,0 +1,20 @@
1
+ module VariableContainer
2
+
3
+ ##
4
+ # Gets a list of variables.
5
+ def get_variables
6
+ json_variables = get_values "#{get_rest_type}/#{@id}/variables"
7
+ json_variables.collect do | variable_json |
8
+ Variable.new variable_json
9
+ end
10
+ end
11
+
12
+ ##
13
+ # Gets a list of variable groups.
14
+ def get_variable_groups
15
+ json_variable_groups = get_values "#{get_rest_type}/#{@id}/#{get_variable_group_type}"
16
+ json_variable_groups.collect do | variable_group_json |
17
+ VariableGroup.new variable_group_json
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ require 'nesstar-api/nesstar-object.rb'
2
+ require 'nesstar-api/variable-container.rb'
3
+
4
+ ##
5
+ # Represents a group of variables in a +Study+.
6
+ class VariableGroup < NesstarObject
7
+ include VariableContainer
8
+
9
+ attr_reader :id, :name
10
+
11
+ def initialize(data)
12
+ @id = data['id']
13
+ @name = data['name']
14
+ end
15
+
16
+ private
17
+
18
+ def get_variable_group_type
19
+ 'child-groups'
20
+ end
21
+
22
+ def get_rest_type
23
+ 'variable-group'
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ require 'nesstar-api/nesstar-object.rb'
2
+ require 'nesstar-api/category.rb'
3
+
4
+ ##
5
+ # Represents a variable in a +Study+.
6
+ class Variable < NesstarObject
7
+ attr_reader :id, :name, :label
8
+
9
+ def initialize(data, eager = false)
10
+ @id = data['id']
11
+ @name = data['name']
12
+ @label = data['label']
13
+ if eager
14
+ load_metadata
15
+ end
16
+ end
17
+
18
+ ##
19
+ # Indicates whether the variable is discrete.
20
+ def discrete?
21
+ metadata['intrvl'] == 'discrete'
22
+ end
23
+
24
+ ##
25
+ # Indicates whether the variable is continuous.
26
+ def continuous?
27
+ metadata['intrvl'] == 'continuous'
28
+ end
29
+
30
+ ##
31
+ # Indicates whether this is a weight variable
32
+ def weighted?
33
+ metadata['wgt']
34
+ end
35
+
36
+ ##
37
+ # Returns a list of categories in this variable
38
+ def get_categories
39
+ metadata['catgry'].collect do | category |
40
+ Category.new category
41
+ end
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nesstar_rest_api_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ricco Førgaard
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-18 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: ricco@fiskeben.dk
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/nesstar-api.rb
21
+ - lib/nesstar-api/variable.rb
22
+ - lib/nesstar-api/category.rb
23
+ - lib/nesstar-api/variable-group.rb
24
+ - lib/nesstar-api/server.rb
25
+ - lib/nesstar-api/variable-container.rb
26
+ - lib/nesstar-api/study.rb
27
+ - lib/nesstar-api/tabulation.rb
28
+ - lib/nesstar-api/rest-client.rb
29
+ - lib/nesstar-api/nesstar-object.rb
30
+ - LICENSE
31
+ - README.md
32
+ homepage: http://www.nesstar.com
33
+ licenses: []
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 1.8.23
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: Client for Nesstar REST API
56
+ test_files: []