quandl_client 2.7.5 → 2.7.6
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/.gitignore +7 -7
- data/.rspec +1 -1
- data/.travis.yml +20 -20
- data/.yardopts +2 -2
- data/Gemfile +12 -12
- data/Guardfile +8 -8
- data/LICENSE +7 -7
- data/README.md +303 -303
- data/Rakefile +31 -35
- data/UPGRADE.md +190 -213
- data/VERSION +1 -1
- data/examples/create.rb +32 -32
- data/examples/find.rb +17 -17
- data/examples/login.rb +12 -12
- data/examples/search.rb +12 -12
- data/examples/trims.rb +15 -15
- data/lib/quandl/client.rb +49 -49
- data/lib/quandl/client/base.rb +91 -91
- data/lib/quandl/client/base/attributes.rb +15 -15
- data/lib/quandl/client/base/model.rb +40 -40
- data/lib/quandl/client/base/search.rb +74 -74
- data/lib/quandl/client/base/validation.rb +101 -101
- data/lib/quandl/client/middleware.rb +9 -9
- data/lib/quandl/client/middleware/parse_json.rb +85 -85
- data/lib/quandl/client/models/dataset.rb +261 -245
- data/lib/quandl/client/models/dataset/data.rb +57 -57
- data/lib/quandl/client/models/location.rb +10 -10
- data/lib/quandl/client/models/report.rb +14 -14
- data/lib/quandl/client/models/scraper.rb +16 -16
- data/lib/quandl/client/models/sheet.rb +50 -50
- data/lib/quandl/client/models/source.rb +48 -40
- data/lib/quandl/client/models/superset.rb +59 -59
- data/lib/quandl/client/models/user.rb +7 -7
- data/lib/quandl/client/version.rb +14 -14
- data/lib/quandl/her/remove_method_data.rb +8 -8
- data/lib/quandl/pattern.rb +37 -37
- data/lib/quandl/pattern/client.rb +8 -8
- data/quandl_client.gemspec +33 -33
- data/spec/factories/dataset.rb +10 -10
- data/spec/factories/sheet.rb +7 -7
- data/spec/factories/source.rb +9 -9
- data/spec/fixtures/scraper.rb +5 -5
- data/spec/lib/quandl/client/dataset/attributes_spec.rb +63 -63
- data/spec/lib/quandl/client/dataset/data_spec.rb +92 -92
- data/spec/lib/quandl/client/dataset/location_spec.rb +65 -65
- data/spec/lib/quandl/client/dataset/persistence_spec.rb +104 -104
- data/spec/lib/quandl/client/dataset/search_spec.rb +19 -19
- data/spec/lib/quandl/client/dataset/source_spec.rb +47 -47
- data/spec/lib/quandl/client/dataset/trim_spec.rb +35 -35
- data/spec/lib/quandl/client/dataset/validation_spec.rb +68 -68
- data/spec/lib/quandl/client/dataset_spec.rb +57 -57
- data/spec/lib/quandl/client/scraper_spec.rb +71 -71
- data/spec/lib/quandl/client/sheet_spec.rb +37 -37
- data/spec/lib/quandl/client/source_spec.rb +51 -51
- data/spec/spec_helper.rb +30 -30
- metadata +27 -5
@@ -1,10 +1,10 @@
|
|
1
|
-
require 'faraday'
|
2
|
-
require 'quandl/client/middleware/parse_json'
|
3
|
-
|
4
|
-
module Quandl
|
5
|
-
module Client
|
6
|
-
module Middleware
|
7
|
-
|
8
|
-
end
|
9
|
-
end
|
1
|
+
require 'faraday'
|
2
|
+
require 'quandl/client/middleware/parse_json'
|
3
|
+
|
4
|
+
module Quandl
|
5
|
+
module Client
|
6
|
+
module Middleware
|
7
|
+
|
8
|
+
end
|
9
|
+
end
|
10
10
|
end
|
@@ -1,86 +1,86 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module Quandl
|
4
|
-
module Client
|
5
|
-
module Middleware
|
6
|
-
|
7
|
-
class ParseJSON < Faraday::Response::Middleware
|
8
|
-
|
9
|
-
def on_complete(env)
|
10
|
-
env[:body] = case env[:status]
|
11
|
-
when 204
|
12
|
-
parse('{}', env)
|
13
|
-
else
|
14
|
-
parse(env[:body], env)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def parse(body, env)
|
19
|
-
json = parse_json(body, env)
|
20
|
-
json.has_key?(:docs) ? format_collection( json, env ) : format_record( json, env )
|
21
|
-
end
|
22
|
-
|
23
|
-
def format_record(json, env)
|
24
|
-
errors = json.delete(:errors) || {}
|
25
|
-
metadata = json.delete(:metadata) || {}
|
26
|
-
# collect some response data
|
27
|
-
metadata.merge!({
|
28
|
-
status: env[:status],
|
29
|
-
headers: env[:response_headers],
|
30
|
-
})
|
31
|
-
# return object
|
32
|
-
object = {
|
33
|
-
:data => json,
|
34
|
-
:errors => errors,
|
35
|
-
:metadata => metadata
|
36
|
-
}
|
37
|
-
env[:status] = 200
|
38
|
-
object
|
39
|
-
end
|
40
|
-
|
41
|
-
def format_collection(json, env)
|
42
|
-
errors = json.delete(:errors) || {}
|
43
|
-
metadata = json.delete(:metadata) || {}
|
44
|
-
docs = json.delete(:docs)
|
45
|
-
# collect some response data
|
46
|
-
metadata.merge!(json).merge!({
|
47
|
-
status: env[:status],
|
48
|
-
headers: env[:response_headers],
|
49
|
-
})
|
50
|
-
# each doc metadata references metadata
|
51
|
-
docs.each{|d| d[:metadata] = metadata }
|
52
|
-
# return object
|
53
|
-
object = {
|
54
|
-
:data => docs,
|
55
|
-
:errors => errors,
|
56
|
-
:metadata => metadata
|
57
|
-
}
|
58
|
-
env[:status] = 200
|
59
|
-
object
|
60
|
-
end
|
61
|
-
|
62
|
-
def parse_json(body = nil, env)
|
63
|
-
body ||= '{}'
|
64
|
-
json = begin
|
65
|
-
JSON.parse(body).symbolize_keys!
|
66
|
-
rescue JSON::ParserError
|
67
|
-
nil
|
68
|
-
end
|
69
|
-
# invalid json body?
|
70
|
-
if json.blank?
|
71
|
-
# fallback to error message
|
72
|
-
json = {
|
73
|
-
id: 1,
|
74
|
-
errors: {
|
75
|
-
parse_errors: [ "Quandl::Client::ParseJSON error. status: #{env[:status]}, body: #{body.inspect}" ]
|
76
|
-
}
|
77
|
-
}
|
78
|
-
end
|
79
|
-
json
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Quandl
|
4
|
+
module Client
|
5
|
+
module Middleware
|
6
|
+
|
7
|
+
class ParseJSON < Faraday::Response::Middleware
|
8
|
+
|
9
|
+
def on_complete(env)
|
10
|
+
env[:body] = case env[:status]
|
11
|
+
when 204
|
12
|
+
parse('{}', env)
|
13
|
+
else
|
14
|
+
parse(env[:body], env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(body, env)
|
19
|
+
json = parse_json(body, env)
|
20
|
+
json.has_key?(:docs) ? format_collection( json, env ) : format_record( json, env )
|
21
|
+
end
|
22
|
+
|
23
|
+
def format_record(json, env)
|
24
|
+
errors = json.delete(:errors) || {}
|
25
|
+
metadata = json.delete(:metadata) || {}
|
26
|
+
# collect some response data
|
27
|
+
metadata.merge!({
|
28
|
+
status: env[:status],
|
29
|
+
headers: env[:response_headers],
|
30
|
+
})
|
31
|
+
# return object
|
32
|
+
object = {
|
33
|
+
:data => json,
|
34
|
+
:errors => errors,
|
35
|
+
:metadata => metadata
|
36
|
+
}
|
37
|
+
env[:status] = 200
|
38
|
+
object
|
39
|
+
end
|
40
|
+
|
41
|
+
def format_collection(json, env)
|
42
|
+
errors = json.delete(:errors) || {}
|
43
|
+
metadata = json.delete(:metadata) || {}
|
44
|
+
docs = json.delete(:docs)
|
45
|
+
# collect some response data
|
46
|
+
metadata.merge!(json).merge!({
|
47
|
+
status: env[:status],
|
48
|
+
headers: env[:response_headers],
|
49
|
+
})
|
50
|
+
# each doc metadata references metadata
|
51
|
+
docs.each{|d| d[:metadata] = metadata }
|
52
|
+
# return object
|
53
|
+
object = {
|
54
|
+
:data => docs,
|
55
|
+
:errors => errors,
|
56
|
+
:metadata => metadata
|
57
|
+
}
|
58
|
+
env[:status] = 200
|
59
|
+
object
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_json(body = nil, env)
|
63
|
+
body ||= '{}'
|
64
|
+
json = begin
|
65
|
+
JSON.parse(body).symbolize_keys!
|
66
|
+
rescue JSON::ParserError
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
# invalid json body?
|
70
|
+
if json.blank?
|
71
|
+
# fallback to error message
|
72
|
+
json = {
|
73
|
+
id: 1,
|
74
|
+
errors: {
|
75
|
+
parse_errors: [ "Quandl::Client::ParseJSON error. status: #{env[:status]}, body: #{body.inspect}" ]
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
json
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
86
|
end
|
@@ -1,246 +1,262 @@
|
|
1
|
-
class Quandl::Client::Dataset < Quandl::Client::Base
|
2
|
-
|
3
|
-
require 'quandl/client/models/dataset/data'
|
4
|
-
|
5
|
-
##########
|
6
|
-
# SCOPES #
|
7
|
-
##########
|
8
|
-
class << self
|
9
|
-
def touch_existing(id)
|
10
|
-
put(File.join(Quandl::Client::Base.url_with_version, "datasets/#{id}/touch")).exists?
|
11
|
-
end
|
12
|
-
|
13
|
-
def find(value)
|
14
|
-
# preformat
|
15
|
-
value = format_id(value)
|
16
|
-
# short-circuit if value is illegal
|
17
|
-
return nil unless value.is_a?(Integer) || value.to_s =~ %r{^#{Quandl::Pattern.full_code}$}
|
18
|
-
# search
|
19
|
-
super(value)
|
20
|
-
end
|
21
|
-
|
22
|
-
def format_id(value)
|
23
|
-
# enforce code formatting
|
24
|
-
if value.is_a?(String)
|
25
|
-
# strip extra whitespace
|
26
|
-
value = value.strip.rstrip
|
27
|
-
# ensure slashes are forward facing
|
28
|
-
value = value.gsub("\\","/").gsub(".","/")
|
29
|
-
# ensure uppercase
|
30
|
-
value = value.upcase
|
31
|
-
end
|
32
|
-
value
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
# SEARCH
|
38
|
-
scope :query, :rows, :owner
|
39
|
-
scope :page, ->(p){ where( page: p.to_i )}
|
40
|
-
scope :source_code, ->(c){ where( code: c.to_s.upcase )}
|
41
|
-
|
42
|
-
###############
|
43
|
-
# ASSOCIATIONS #
|
44
|
-
###############
|
45
|
-
|
46
|
-
def source
|
47
|
-
@source ||= Quandl::Client::Source.find(self.source_code)
|
48
|
-
end
|
49
|
-
|
50
|
-
###############
|
51
|
-
# VALIDATIONS #
|
52
|
-
###############
|
53
|
-
|
54
|
-
validates :code, presence: true, format: { with: Quandl::Pattern.code, message: "is invalid. Expected format: #{Quandl::Pattern.code.to_example}" }
|
55
|
-
validates :display_url, allow_blank: true, url: true
|
56
|
-
validate :data_should_be_valid!
|
57
|
-
validate :dataset_data_should_be_valid!
|
58
|
-
validate :data_row_count_should_match_column_count!
|
59
|
-
validate :data_columns_should_not_exceed_column_names!
|
60
|
-
validate :data_rows_should_have_equal_columns!
|
61
|
-
validate :ambiguous_code_requires_source_code!
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
##############
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
:
|
72
|
-
:
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
return
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@
|
143
|
-
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
self.errors.add( :data, "
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
return
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
1
|
+
class Quandl::Client::Dataset < Quandl::Client::Base
|
2
|
+
|
3
|
+
require 'quandl/client/models/dataset/data'
|
4
|
+
|
5
|
+
##########
|
6
|
+
# SCOPES #
|
7
|
+
##########
|
8
|
+
class << self
|
9
|
+
def touch_existing(id)
|
10
|
+
put(File.join(Quandl::Client::Base.url_with_version, "datasets/#{id}/touch")).exists?
|
11
|
+
end
|
12
|
+
|
13
|
+
def find(value)
|
14
|
+
# preformat
|
15
|
+
value = format_id(value)
|
16
|
+
# short-circuit if value is illegal
|
17
|
+
return nil unless value.is_a?(Integer) || value.to_s =~ %r{^#{Quandl::Pattern.full_code}$}
|
18
|
+
# search
|
19
|
+
super(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def format_id(value)
|
23
|
+
# enforce code formatting
|
24
|
+
if value.is_a?(String)
|
25
|
+
# strip extra whitespace
|
26
|
+
value = value.strip.rstrip
|
27
|
+
# ensure slashes are forward facing
|
28
|
+
value = value.gsub("\\","/").gsub(".","/")
|
29
|
+
# ensure uppercase
|
30
|
+
value = value.upcase
|
31
|
+
end
|
32
|
+
value
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# SEARCH
|
38
|
+
scope :query, :rows, :owner
|
39
|
+
scope :page, ->(p){ where( page: p.to_i )}
|
40
|
+
scope :source_code, ->(c){ where( code: c.to_s.upcase )}
|
41
|
+
|
42
|
+
###############
|
43
|
+
# ASSOCIATIONS #
|
44
|
+
###############
|
45
|
+
|
46
|
+
def source
|
47
|
+
@source ||= Quandl::Client::Source.find(self.source_code)
|
48
|
+
end
|
49
|
+
|
50
|
+
###############
|
51
|
+
# VALIDATIONS #
|
52
|
+
###############
|
53
|
+
|
54
|
+
validates :code, presence: true, format: { with: Quandl::Pattern.code, message: "is invalid. Expected format: #{Quandl::Pattern.code.to_example}" }
|
55
|
+
validates :display_url, allow_blank: true, url: true
|
56
|
+
validate :data_should_be_valid!
|
57
|
+
validate :dataset_data_should_be_valid!
|
58
|
+
validate :data_row_count_should_match_column_count!
|
59
|
+
validate :data_columns_should_not_exceed_column_names!
|
60
|
+
validate :data_rows_should_have_equal_columns!
|
61
|
+
validate :ambiguous_code_requires_source_code!
|
62
|
+
|
63
|
+
validate :source_code_should_exist!
|
64
|
+
|
65
|
+
|
66
|
+
##############
|
67
|
+
# PROPERTIES #
|
68
|
+
##############
|
69
|
+
|
70
|
+
attributes :source_code, :code, :name, :urlize_name,
|
71
|
+
:description, :updated_at, :frequency,
|
72
|
+
:from_date, :to_date, :column_names, :private, :type,
|
73
|
+
:display_url, :column_spec, :import_spec, :import_url,
|
74
|
+
:locations_attributes, :availability_delay, :refreshed_at
|
75
|
+
|
76
|
+
before_save :enforce_required_formats
|
77
|
+
|
78
|
+
after_save :save_dataset_data
|
79
|
+
|
80
|
+
alias_method :locations, :locations_attributes
|
81
|
+
alias_method :locations=, :locations_attributes=
|
82
|
+
|
83
|
+
def reference_url
|
84
|
+
self.display_url
|
85
|
+
end
|
86
|
+
def reference_url=(value)
|
87
|
+
value = "http://#{value}" if value.present? && !(value =~ /:\/\//)
|
88
|
+
self.display_url = value
|
89
|
+
end
|
90
|
+
|
91
|
+
def full_url
|
92
|
+
File.join(Quandl::Client::Base.url.gsub(/api\/?/, ''), full_code)
|
93
|
+
end
|
94
|
+
|
95
|
+
def full_code
|
96
|
+
File.join(self.source_code.to_s, self.code.to_s)
|
97
|
+
end
|
98
|
+
|
99
|
+
# DATA
|
100
|
+
|
101
|
+
def data
|
102
|
+
defined?(@data) ? @data : data_scope
|
103
|
+
end
|
104
|
+
|
105
|
+
def data=(value)
|
106
|
+
@data = Quandl::Data.new(value).sort_descending
|
107
|
+
end
|
108
|
+
|
109
|
+
def data?
|
110
|
+
@data.is_a?(Quandl::Data)
|
111
|
+
end
|
112
|
+
|
113
|
+
def source_code=(v)
|
114
|
+
write_attribute(:source_code, v.to_s.upcase)
|
115
|
+
end
|
116
|
+
|
117
|
+
def delete_data
|
118
|
+
# cant delete unsaved records
|
119
|
+
return false if new_record?
|
120
|
+
# delete and return success / failure
|
121
|
+
self.class.destroy_existing("#{id}/data").saved?
|
122
|
+
end
|
123
|
+
|
124
|
+
def delete_rows(*dates)
|
125
|
+
# cant delete unsaved records
|
126
|
+
return false if new_record?
|
127
|
+
# collect dates
|
128
|
+
query = { dates: Array(dates).flatten }.to_query
|
129
|
+
# delete and return success / failure
|
130
|
+
self.class.destroy_existing("#{id}/data/rows?#{query}").saved?
|
131
|
+
end
|
132
|
+
|
133
|
+
def data_scope
|
134
|
+
@data_scope ||= Quandl::Client::Dataset::Data.with_id(id)
|
135
|
+
end
|
136
|
+
|
137
|
+
def dataset_data
|
138
|
+
@dataset_data ||= Quandl::Client::Dataset::Data.new( id: id )
|
139
|
+
end
|
140
|
+
|
141
|
+
def dataset_data?
|
142
|
+
@dataset_data.is_a?(Quandl::Client::Dataset::Data)
|
143
|
+
end
|
144
|
+
|
145
|
+
def reload
|
146
|
+
@dataset_data = nil
|
147
|
+
@data_scope = nil
|
148
|
+
@full_code = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
protected
|
152
|
+
|
153
|
+
def data_should_be_valid!
|
154
|
+
if data? && !data.valid?
|
155
|
+
data.errors.each{|k,v| self.errors.add( k,v ) }
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
def dataset_data_should_be_valid!
|
162
|
+
if dataset_data? && !dataset_data.valid?
|
163
|
+
dataset_data.errors.each{|k,v| self.errors.add( k,v ) }
|
164
|
+
return false
|
165
|
+
end
|
166
|
+
true
|
167
|
+
end
|
168
|
+
|
169
|
+
def source_code_should_exist!
|
170
|
+
if source_code.present?
|
171
|
+
Source.cached[source_code] = Source.find(source_code) unless Source.cached.has_key?(source_code)
|
172
|
+
source = Source.cached[source_code]
|
173
|
+
self.errors.add( :source_code, "Could not find a source with the source_code '#{source_code}'" ) if source.blank? || source.code.blank?
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
def ambiguous_code_requires_source_code!
|
180
|
+
if code.to_s.numeric? && source_code.blank?
|
181
|
+
message = %Q{Pure numerical codes like "#{code}" are not allowed unless you include a source code. Do this:\nsource_code: <USERNAME>\ncode: #{code}}
|
182
|
+
self.errors.add( :data, message )
|
183
|
+
return false
|
184
|
+
end
|
185
|
+
true
|
186
|
+
end
|
187
|
+
|
188
|
+
def data_columns_should_not_exceed_column_names!
|
189
|
+
if errors.size == 0 && data? && data.present? && column_names.present? && data.first.count != column_names.count
|
190
|
+
self.errors.add( :data, "You may not change the number of columns in a dataset. This dataset has #{column_names.count} columns but you tried to send #{data.first.count} columns." )
|
191
|
+
return false
|
192
|
+
end
|
193
|
+
true
|
194
|
+
end
|
195
|
+
|
196
|
+
def data_rows_should_have_equal_columns!
|
197
|
+
# skip validation unless data is present
|
198
|
+
return true unless data? && data.present?
|
199
|
+
# use first row as expected column count
|
200
|
+
column_count = data[0].count
|
201
|
+
# check each row
|
202
|
+
data.each_with_index do |row, index|
|
203
|
+
# the row is valid if it's count matches the first row's count
|
204
|
+
next if row.count == column_count
|
205
|
+
# the row is invalid if the count is mismatched
|
206
|
+
self.errors.add( :data, "Unexpected number of points in this row:\n#{row.join(',')}\nFound #{row.size-1} but expected #{data[0].size-1} based on precedent from the first row (#{data[0].join(',')})" )
|
207
|
+
# return validation failure
|
208
|
+
return false
|
209
|
+
end
|
210
|
+
true
|
211
|
+
end
|
212
|
+
|
213
|
+
def data_row_count_should_match_column_count!
|
214
|
+
# skip validation unless data and column_names present
|
215
|
+
return true unless data? && data.present? && column_names.present?
|
216
|
+
# count the number of expected columns
|
217
|
+
column_count = column_names.count
|
218
|
+
# check each row
|
219
|
+
data.each_with_index do |row, index|
|
220
|
+
# the row is valid if it's count matches the first row's count
|
221
|
+
next if row.count == column_count
|
222
|
+
# the row is invalid if the count is mismatched
|
223
|
+
self.errors.add( :data, "Unexpected number of points in this row:\n#{row.join(',')}\nFound #{row.size-1} but expected #{column_names.count-1} based on precedent from the header row (#{column_names.join(',')})" )
|
224
|
+
# return validation failure
|
225
|
+
return false
|
226
|
+
end
|
227
|
+
true
|
228
|
+
end
|
229
|
+
|
230
|
+
def save_dataset_data
|
231
|
+
return if (!saved? && id.blank?)
|
232
|
+
return if !data? || data.blank?
|
233
|
+
|
234
|
+
dataset_data.id = id
|
235
|
+
dataset_data.data = data.to_csv
|
236
|
+
dataset_data.save
|
237
|
+
# update dataset's attributes with dataset_data's attributes
|
238
|
+
attributes.each{|k,v| attributes[k] = dataset_data.attributes[k] if dataset_data.attributes.has_key?(k) }
|
239
|
+
# update dataset errors with dataset_data
|
240
|
+
@metadata[:status] = dataset_data.status unless dataset_data.saved?
|
241
|
+
# inherit_errors(dataset_data) unless dataset_data.saved?
|
242
|
+
end
|
243
|
+
|
244
|
+
def inherit_errors(object)
|
245
|
+
return unless object.respond_to?(:response_errors) && object.response_errors.respond_to?(:each)
|
246
|
+
object.response_errors.each do |key, messages|
|
247
|
+
if messages.respond_to?(:each)
|
248
|
+
messages.each{|message| errors.add(key, message) }
|
249
|
+
end
|
250
|
+
end
|
251
|
+
@metadata[:status] = object.status
|
252
|
+
object
|
253
|
+
end
|
254
|
+
|
255
|
+
def enforce_required_formats
|
256
|
+
# self.data = Quandl::Data.new(data).to_csv
|
257
|
+
self.source_code = self.source_code.to_s.upcase
|
258
|
+
self.code = self.code.to_s.upcase
|
259
|
+
self.locations_attributes = locations_attributes.to_json if locations_attributes.respond_to?(:to_json) && !locations_attributes.kind_of?(String)
|
260
|
+
end
|
261
|
+
|
246
262
|
end
|