gooddata 0.6.0.pre6 → 0.6.0.pre7
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/bin/gooddata +102 -56
- data/gooddata.gemspec +3 -0
- data/lib/gooddata/bricks/base_downloader.rb +62 -0
- data/lib/gooddata/bricks/brick.rb +24 -25
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +38 -0
- data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +4 -2
- data/lib/gooddata/bricks/middleware/restforce_middleware.rb +9 -10
- data/lib/gooddata/client.rb +21 -1
- data/lib/gooddata/commands/auth.rb +68 -60
- data/lib/gooddata/commands/process.rb +75 -62
- data/lib/gooddata/commands/runners.rb +9 -6
- data/lib/gooddata/connection.rb +45 -20
- data/lib/gooddata/helpers.rb +31 -4
- data/lib/gooddata/model.rb +199 -72
- data/lib/gooddata/models/data_result.rb +94 -101
- data/lib/gooddata/models/metadata.rb +31 -9
- data/lib/gooddata/models/metric.rb +1 -1
- data/lib/gooddata/models/process.rb +2 -59
- data/lib/gooddata/models/project.rb +1 -1
- data/lib/gooddata/models/project_metadata.rb +10 -1
- data/lib/gooddata/models/report.rb +0 -3
- data/lib/gooddata/models/report_definition.rb +43 -8
- data/lib/gooddata/version.rb +1 -1
- data/spec/full_project_spec.rb +54 -0
- data/spec/goodzilla_spec.rb +2 -2
- data/spec/model_dsl_spec.rb +2 -1
- data/spec/test_project_model_spec.json +86 -0
- metadata +53 -2
- data/TODO.md +0 -77
data/lib/gooddata/helpers.rb
CHANGED
@@ -1,18 +1,45 @@
|
|
1
1
|
module GoodData::Helpers
|
2
|
-
def home_directory
|
2
|
+
def self.home_directory
|
3
3
|
running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
|
4
4
|
end
|
5
5
|
|
6
|
-
def running_on_windows?
|
6
|
+
def self.running_on_windows?
|
7
7
|
RUBY_PLATFORM =~ /mswin32|mingw32/
|
8
8
|
end
|
9
9
|
|
10
|
-
def running_on_a_mac?
|
10
|
+
def self.running_on_a_mac?
|
11
11
|
RUBY_PLATFORM =~ /-darwin\d/
|
12
12
|
end
|
13
13
|
|
14
|
-
def error(msg)
|
14
|
+
def self.error(msg)
|
15
15
|
STDERR.puts(msg)
|
16
16
|
exit 1
|
17
17
|
end
|
18
|
+
|
19
|
+
def self.find_goodfile(pwd, options={})
|
20
|
+
root = Pathname(options[:root] || '/' )
|
21
|
+
pwd = Pathname(pwd).expand_path
|
22
|
+
begin
|
23
|
+
gf = pwd + "Goodfile"
|
24
|
+
if gf.exist?
|
25
|
+
return gf
|
26
|
+
end
|
27
|
+
pwd = pwd.parent
|
28
|
+
end until root == pwd
|
29
|
+
fail "Goodfile not found in #{pwd.to_s} or any parent up to #{root.to_s}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.hash_dfs(thing, &block)
|
33
|
+
if !thing.is_a?(Hash) && !thing.is_a?(Array)
|
34
|
+
elsif thing.is_a?(Array)
|
35
|
+
thing.each do |child|
|
36
|
+
hash_dfs(child, &block)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
thing.each do |key, val|
|
40
|
+
yield(thing, key)
|
41
|
+
hash_dfs(val, &block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
18
45
|
end
|
data/lib/gooddata/model.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'open-uri'
|
2
|
-
|
2
|
+
require 'active_support/all'
|
3
3
|
##
|
4
4
|
# Module containing classes that counter-part GoodData server-side meta-data
|
5
5
|
# elements, including the server-side data model.
|
@@ -49,6 +49,134 @@ module GoodData
|
|
49
49
|
GoodData.post ldm_uri, { 'manage' => { 'maql' => schema.to_maql_create } }
|
50
50
|
end
|
51
51
|
|
52
|
+
# Load given file into a data set described by the given schema
|
53
|
+
def upload_data(path, manifest, options={})
|
54
|
+
project = options[:project] || GoodData.project
|
55
|
+
# mode = options[:mode] || "FULL"
|
56
|
+
path = path.path if path.respond_to? :path
|
57
|
+
inline_data = path.is_a?(String) ? false : true
|
58
|
+
|
59
|
+
# create a temporary zip file
|
60
|
+
dir = Dir.mktmpdir
|
61
|
+
begin
|
62
|
+
Zip::File.open("#{dir}/upload.zip", Zip::File::CREATE) do |zip|
|
63
|
+
# TODO make sure schema columns match CSV column names
|
64
|
+
zip.get_output_stream('upload_info.json') { |f| f.puts JSON.pretty_generate(manifest) }
|
65
|
+
if inline_data
|
66
|
+
zip.get_output_stream('data.csv') do |f|
|
67
|
+
path.each do |row|
|
68
|
+
f.puts row.to_csv
|
69
|
+
end
|
70
|
+
end
|
71
|
+
else
|
72
|
+
zip.add('data.csv', path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# upload it
|
77
|
+
GoodData.upload_to_user_webdav("#{dir}/upload.zip", :directory => File.basename(dir))
|
78
|
+
ensure
|
79
|
+
FileUtils.rm_rf dir
|
80
|
+
end
|
81
|
+
|
82
|
+
# kick the load
|
83
|
+
pull = { 'pullIntegration' => File.basename(dir) }
|
84
|
+
link = project.md.links('etl')['pull']
|
85
|
+
task = GoodData.post link, pull
|
86
|
+
while (GoodData.get(task["pullTask"]["uri"])["taskStatus"] === "RUNNING" || GoodData.get(task["pullTask"]["uri"])["taskStatus"] === "PREPARED") do
|
87
|
+
sleep 30
|
88
|
+
end
|
89
|
+
if (GoodData.get(task["pullTask"]["uri"])["taskStatus"] == "ERROR")
|
90
|
+
s = StringIO.new
|
91
|
+
GoodData.download_form_user_webdav(File.basename(dir) + '/upload_status.json', s)
|
92
|
+
js = JSON.parse(s.string)
|
93
|
+
fail "Load Failed with error #{JSON.pretty_generate(js)}"
|
94
|
+
end
|
95
|
+
puts "Done loading"
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
class ProjectBlueprint
|
101
|
+
|
102
|
+
attr_accessor :data
|
103
|
+
|
104
|
+
def change(&block)
|
105
|
+
builder = ProjectBuilder.create_from_data(self)
|
106
|
+
block.call(builder)
|
107
|
+
builder
|
108
|
+
@data = builder.to_hash
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def datasets
|
113
|
+
data[:datasets].map {|d| SchemaBlueprint.new(d)}
|
114
|
+
end
|
115
|
+
|
116
|
+
def get_dataset(name)
|
117
|
+
ds = data[:datasets].find {|d| d[:name] == name}
|
118
|
+
SchemaBlueprint.new(ds) unless ds.nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(init_data)
|
122
|
+
@data = init_data
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
class SchemaBlueprint
|
128
|
+
|
129
|
+
attr_accessor :data
|
130
|
+
|
131
|
+
def change(&block)
|
132
|
+
builder = SchemaBuilder.create_from_data(self)
|
133
|
+
block.call(builder)
|
134
|
+
builder
|
135
|
+
@data = builder.to_hash
|
136
|
+
self
|
137
|
+
end
|
138
|
+
|
139
|
+
def initialize(init_data)
|
140
|
+
@data = init_data
|
141
|
+
end
|
142
|
+
|
143
|
+
def upload(source, options={})
|
144
|
+
project = options[:project] || GoodData.project
|
145
|
+
mode = options[:load] || "FULL"
|
146
|
+
project.upload(source, to_schema, mode)
|
147
|
+
end
|
148
|
+
|
149
|
+
def name
|
150
|
+
data[:name]
|
151
|
+
end
|
152
|
+
|
153
|
+
def title
|
154
|
+
data[:title]
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_hash
|
158
|
+
data
|
159
|
+
end
|
160
|
+
|
161
|
+
def columns
|
162
|
+
data[:columns]
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_schema
|
166
|
+
Schema.new(to_hash)
|
167
|
+
end
|
168
|
+
|
169
|
+
def to_manifest
|
170
|
+
to_schema.to_manifest
|
171
|
+
end
|
172
|
+
|
173
|
+
def pretty_print(printer)
|
174
|
+
printer.text "Schema <#{object_id}>:\n"
|
175
|
+
printer.text " Name: #{name}\n"
|
176
|
+
printer.text " Columns: \n"
|
177
|
+
printer.text columns.map {|c| " #{c[:name]}: #{c[:type]}"}.join("\n")
|
178
|
+
end
|
179
|
+
|
52
180
|
end
|
53
181
|
|
54
182
|
class ProjectBuilder
|
@@ -56,13 +184,19 @@ module GoodData
|
|
56
184
|
attr_reader :title, :datasets, :reports, :metrics, :uploads, :users, :assert_report, :date_dimensions
|
57
185
|
|
58
186
|
class << self
|
59
|
-
|
187
|
+
|
188
|
+
def create_from_data(blueprint)
|
189
|
+
pb = ProjectBuilder.new
|
190
|
+
pb.data = blueprint.to_hash
|
191
|
+
pb
|
192
|
+
end
|
193
|
+
|
60
194
|
def create(title, options={}, &block)
|
61
195
|
pb = ProjectBuilder.new(title)
|
62
196
|
block.call(pb)
|
63
197
|
pb
|
64
198
|
end
|
65
|
-
|
199
|
+
|
66
200
|
end
|
67
201
|
|
68
202
|
def initialize(title)
|
@@ -129,8 +263,14 @@ module GoodData
|
|
129
263
|
@users << users
|
130
264
|
end
|
131
265
|
|
132
|
-
def to_json
|
133
|
-
|
266
|
+
def to_json(options={})
|
267
|
+
eliminate_empty = options[:eliminate_empty] || false
|
268
|
+
|
269
|
+
if eliminate_empty
|
270
|
+
JSON.pretty_generate(to_hash.reject {|k, v| v.is_a?(Enumerable) && v.empty?})
|
271
|
+
else
|
272
|
+
JSON.pretty_generate(to_hash)
|
273
|
+
end
|
134
274
|
end
|
135
275
|
|
136
276
|
def to_hash
|
@@ -147,10 +287,14 @@ module GoodData
|
|
147
287
|
}
|
148
288
|
end
|
149
289
|
|
290
|
+
def get_dataset(name)
|
291
|
+
datasets.find {|d| d.name == name}
|
292
|
+
end
|
293
|
+
|
150
294
|
end
|
151
295
|
|
152
296
|
class DashboardBuilder
|
153
|
-
|
297
|
+
|
154
298
|
def initialize(title)
|
155
299
|
@title = title
|
156
300
|
@tabs = []
|
@@ -172,7 +316,7 @@ module GoodData
|
|
172
316
|
end
|
173
317
|
|
174
318
|
class TabBuilder
|
175
|
-
|
319
|
+
|
176
320
|
def initialize(title)
|
177
321
|
@title = title
|
178
322
|
@stuff = []
|
@@ -193,15 +337,35 @@ module GoodData
|
|
193
337
|
|
194
338
|
class SchemaBuilder
|
195
339
|
|
196
|
-
attr_accessor :
|
340
|
+
attr_accessor :data
|
341
|
+
|
342
|
+
class << self
|
343
|
+
|
344
|
+
def create_from_data(blueprint)
|
345
|
+
sc = SchemaBuilder.new
|
346
|
+
sc.data = blueprint.to_hash
|
347
|
+
sc
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
197
351
|
|
198
352
|
def initialize(name=nil)
|
199
|
-
@
|
200
|
-
|
353
|
+
@data = {
|
354
|
+
:name => name,
|
355
|
+
:columns => []
|
356
|
+
}
|
357
|
+
end
|
358
|
+
|
359
|
+
def name
|
360
|
+
data[:name]
|
361
|
+
end
|
362
|
+
|
363
|
+
def columns
|
364
|
+
data[:columns]
|
201
365
|
end
|
202
366
|
|
203
367
|
def add_column(column_def)
|
204
|
-
|
368
|
+
columns.push(column_def)
|
205
369
|
self
|
206
370
|
end
|
207
371
|
|
@@ -233,21 +397,18 @@ module GoodData
|
|
233
397
|
add_column({ :type => :reference, :name => name}.merge(options))
|
234
398
|
end
|
235
399
|
|
236
|
-
def to_schema
|
237
|
-
Schema.new(to_hash)
|
238
|
-
end
|
239
|
-
|
240
400
|
def to_json
|
241
401
|
JSON.pretty_generate(to_hash)
|
242
402
|
end
|
243
403
|
|
244
404
|
def to_hash
|
245
|
-
|
246
|
-
:name => @name,
|
247
|
-
:columns => @columns
|
248
|
-
}
|
249
|
-
h.has_key?(:title) ? h.merge({:title => h[:title]}) : h
|
405
|
+
data
|
250
406
|
end
|
407
|
+
|
408
|
+
def to_schema
|
409
|
+
Schema.new(to_hash)
|
410
|
+
end
|
411
|
+
|
251
412
|
end
|
252
413
|
|
253
414
|
class ProjectCreator
|
@@ -257,20 +418,20 @@ module GoodData
|
|
257
418
|
|
258
419
|
spec = options[:spec] || fail("You need to provide spec for migration")
|
259
420
|
spec = spec.to_hash
|
260
|
-
|
421
|
+
|
261
422
|
token = options[:token] || fail("You need to specify token for project creation")
|
262
|
-
|
423
|
+
project = options[:project] || GoodData::Project.create(:title => spec[:title], :auth_token => token)
|
263
424
|
|
264
425
|
begin
|
265
|
-
GoodData.with_project(
|
266
|
-
migrate_date_dimensions(p, spec[:date_dimensions])
|
267
|
-
migrate_datasets(p, spec[:datasets])
|
426
|
+
GoodData.with_project(project) do |p|
|
427
|
+
migrate_date_dimensions(p, spec[:date_dimensions] || [])
|
428
|
+
migrate_datasets(p, spec[:datasets] || [])
|
268
429
|
load(p, spec)
|
269
|
-
migrate_metrics(p, spec[:metrics])
|
270
|
-
migrate_reports(p, spec[:reports])
|
271
|
-
migrate_dashboards(p, spec[:dashboards])
|
272
|
-
migrate_users(p, spec[:users])
|
273
|
-
execute_tests(p, spec[:assert_tests])
|
430
|
+
migrate_metrics(p, spec[:metrics] || [])
|
431
|
+
migrate_reports(p, spec[:reports] || [])
|
432
|
+
migrate_dashboards(p, spec[:dashboards] || [])
|
433
|
+
migrate_users(p, spec[:users] || [])
|
434
|
+
execute_tests(p, spec[:assert_tests] || [])
|
274
435
|
p
|
275
436
|
end
|
276
437
|
end
|
@@ -284,7 +445,9 @@ module GoodData
|
|
284
445
|
|
285
446
|
def migrate_datasets(project, spec)
|
286
447
|
spec.each do |ds|
|
287
|
-
|
448
|
+
schema = GoodData::Model::Schema.new(ds)
|
449
|
+
project.add_dataset(schema)
|
450
|
+
GoodData::ProjectMetadata["manifest_#{schema.name}"] = schema.to_manifest.to_json
|
288
451
|
end
|
289
452
|
end
|
290
453
|
|
@@ -459,55 +622,20 @@ module GoodData
|
|
459
622
|
Tempfile.open('remote_file') do |temp|
|
460
623
|
temp << open(path).read
|
461
624
|
temp.flush
|
462
|
-
upload_data(temp,
|
625
|
+
upload_data(temp, mode)
|
463
626
|
end
|
464
627
|
else
|
465
|
-
upload_data(path,
|
628
|
+
upload_data(path, mode)
|
466
629
|
end
|
467
630
|
end
|
468
631
|
|
469
|
-
|
470
|
-
|
471
|
-
path = path.path if path.respond_to? :path
|
472
|
-
|
473
|
-
inline_data = path.is_a?(String) ? false : true
|
474
|
-
|
475
|
-
project = GoodData.project unless project
|
476
|
-
|
477
|
-
# create a temporary zip file
|
478
|
-
dir = Dir.mktmpdir
|
479
|
-
Zip::File.open("#{dir}/upload.zip", Zip::File::CREATE) do |zip|
|
480
|
-
# TODO make sure schema columns match CSV column names
|
481
|
-
zip.get_output_stream('upload_info.json') { |f| f.puts JSON.pretty_generate(to_manifest(mode)) }
|
482
|
-
if inline_data
|
483
|
-
zip.get_output_stream('data.csv') do |f|
|
484
|
-
path.each do |row|
|
485
|
-
f.puts row.to_csv
|
486
|
-
end
|
487
|
-
end
|
488
|
-
else
|
489
|
-
zip.add('data.csv', path)
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
|
-
# upload it
|
494
|
-
GoodData.upload_to_user_webdav("#{dir}/upload.zip", :directory => File.basename(dir))
|
495
|
-
FileUtils.rm_rf dir
|
496
|
-
|
497
|
-
# kick the load
|
498
|
-
pull = { 'pullIntegration' => File.basename(dir) }
|
499
|
-
link = project.md.links('etl')['pull']
|
500
|
-
task = GoodData.post link, pull
|
501
|
-
while (GoodData.get(task["pullTask"]["uri"])["taskStatus"] === "RUNNING" || GoodData.get(task["pullTask"]["uri"])["taskStatus"] === "PREPARED") do
|
502
|
-
sleep 30
|
503
|
-
end
|
504
|
-
fail "Load Failed" if (GoodData.get(task["pullTask"]["uri"])["taskStatus"] == "ERROR")
|
505
|
-
puts "Done loading"
|
632
|
+
def upload_data(path, mode)
|
633
|
+
GoodData::Model.upload_data(path, to_manifest(mode))
|
506
634
|
end
|
507
635
|
|
508
636
|
# Generates the SLI manifest describing the data loading
|
509
637
|
#
|
510
|
-
def to_manifest(mode)
|
638
|
+
def to_manifest(mode="FULL")
|
511
639
|
{
|
512
640
|
'dataSetSLIManifest' => {
|
513
641
|
'parts' => fields.reduce([]) { |memo, f| val = f.to_manifest_part(mode); memo << val unless val.nil?; memo },
|
@@ -1024,7 +1152,6 @@ module GoodData
|
|
1024
1152
|
@title = spec[:title] || @name
|
1025
1153
|
@urn = spec[:urn] || "URN:GOODDATA:DATE"
|
1026
1154
|
end
|
1027
|
-
|
1028
1155
|
|
1029
1156
|
def to_maql_create
|
1030
1157
|
# urn = "urn:chefs_warehouse_fiscal:date"
|
@@ -1,36 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class Row < CSV::Row
|
4
|
-
def ==(other)
|
5
|
-
len = length()
|
6
|
-
return false if len != other.length
|
7
|
-
result = true
|
8
|
-
|
9
|
-
len.times do |i|
|
10
|
-
result = false unless convert_field(field(i)) == convert_field(other.field(i))
|
11
|
-
end
|
12
|
-
result
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
def convert_field(val)
|
17
|
-
if val.is_a?(String) && val.match(/^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/)
|
18
|
-
# Is it a Number?
|
19
|
-
val = val.scan(/[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?/).first
|
20
|
-
val = val.include?('.') ? val.to_f.round : val.to_i
|
21
|
-
return val
|
22
|
-
elsif val.nil? || (val.respond_to?(:strip) && val.strip.empty?)
|
23
|
-
#is ia a String
|
24
|
-
return ''
|
25
|
-
elsif val.respond_to? :round
|
26
|
-
# No idea what that one does
|
27
|
-
return val.round
|
28
|
-
else
|
29
|
-
return val
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
1
|
+
class BigDecimal; def pretty_print(p) p.text to_s; end; end
|
33
2
|
|
3
|
+
module GoodData
|
4
|
+
|
34
5
|
class DataResult
|
35
6
|
|
36
7
|
attr_reader :data
|
@@ -43,14 +14,17 @@ module GoodData
|
|
43
14
|
puts to_s
|
44
15
|
end
|
45
16
|
|
46
|
-
def to_s
|
17
|
+
def to_s(options={})
|
18
|
+
with_indices = options[:index] || false
|
47
19
|
a = to_table.to_a
|
48
|
-
a.transpose
|
49
|
-
|
20
|
+
data = a.transpose
|
21
|
+
data.unshift((1..a.length).to_a) if with_indices
|
22
|
+
data.each_with_index.map{|col, i|
|
23
|
+
col.unshift(i.zero? ? nil : i) if with_indices # inserts row labels #
|
50
24
|
w = col.map{|cell| cell.to_s.length}.max # w = "column width" #
|
51
25
|
col.each_with_index.map{|cell, i|
|
52
|
-
i.zero
|
53
|
-
}.transpose.map{|row| "[#{row.join(' | ')}]"}.join("\n")
|
26
|
+
i.zero? ? cell.to_s.center(w) : cell.to_s.ljust(w)} # alligns the column #
|
27
|
+
}.transpose.map{|row| "[#{row.join(' | ')}]"}.unshift("").join("\n")
|
54
28
|
end
|
55
29
|
|
56
30
|
def to_table
|
@@ -72,7 +46,8 @@ module GoodData
|
|
72
46
|
end
|
73
47
|
|
74
48
|
def assemble_table
|
75
|
-
@table =
|
49
|
+
@table = [[]]
|
50
|
+
# CSV::Table.new([GoodData::Row.new([],[],false)])
|
76
51
|
end
|
77
52
|
|
78
53
|
def to_table
|
@@ -92,69 +67,71 @@ module GoodData
|
|
92
67
|
end
|
93
68
|
end
|
94
69
|
|
95
|
-
class SFDataResult < DataResult
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
end
|
70
|
+
# class SFDataResult < DataResult
|
71
|
+
#
|
72
|
+
# def initialize(data, options = {})
|
73
|
+
# super(data)
|
74
|
+
# @options = options
|
75
|
+
# assemble_table
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# def assemble_table
|
79
|
+
# sf_data = data[:queryResponse][:result][:records]
|
80
|
+
# sf_data = sf_data.is_a?(Hash) ? [sf_data] : sf_data
|
81
|
+
# if @options[:soql]
|
82
|
+
# # puts @options[:soql]
|
83
|
+
# fields = @options[:soql].strip.match(/SELECT (.*) FROM/i)[1]
|
84
|
+
# @headers = fields.strip.split(",").map do |item|
|
85
|
+
# item.strip.split(/\s/)
|
86
|
+
# end.map do |item|
|
87
|
+
# item.last.to_sym
|
88
|
+
# end
|
89
|
+
# # pp @headers
|
90
|
+
# elsif @options[:headers]
|
91
|
+
# @headers = @options[:headers]
|
92
|
+
# else
|
93
|
+
# @headers = sf_data.first.keys - [:type, :Id]
|
94
|
+
# end
|
95
|
+
# @table = CSV::Table.new(sf_data.collect do |line|
|
96
|
+
# GoodData::Row.new([], @headers.map {|h| line[h] || ' '}, false)
|
97
|
+
# end)
|
98
|
+
# rescue
|
99
|
+
# fail "Unable to assemble the table. Either the data provided are empty or the SOQL is malformed."
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# def to_table
|
103
|
+
# @table
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# def == (otherDataResult)
|
107
|
+
# result = true
|
108
|
+
# len = @table.length
|
109
|
+
# other_table = otherDataResult.to_table
|
110
|
+
# if len != other_table.length
|
111
|
+
# # puts "TABLES ARE OF DIFFERENT SIZES"
|
112
|
+
# return false
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# diff(otherDataResult).empty?() ? true : false
|
116
|
+
#
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# def diff(otherDataResult)
|
120
|
+
# other_table = otherDataResult.to_table
|
121
|
+
# differences = []
|
122
|
+
#
|
123
|
+
# @table.each do |row|
|
124
|
+
# differences << row unless other_table.detect {|r| r == row}
|
125
|
+
# end
|
126
|
+
# differences
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# end
|
155
130
|
|
156
131
|
class ReportDataResult < DataResult
|
157
132
|
|
133
|
+
ROW_LIMIT = 10000000
|
134
|
+
|
158
135
|
attr_reader :row_headers, :column_headers, :table, :headers_height, :headers_width
|
159
136
|
|
160
137
|
def initialize(data)
|
@@ -170,16 +147,32 @@ module GoodData
|
|
170
147
|
end
|
171
148
|
|
172
149
|
def without_column_headers
|
173
|
-
@table = table.transpose[headers_height,
|
150
|
+
@table = table.transpose[headers_height, ROW_LIMIT].transpose
|
174
151
|
self
|
175
152
|
end
|
176
153
|
|
154
|
+
def to_data_table
|
155
|
+
table.transpose[headers_height, ROW_LIMIT].transpose[headers_width, ROW_LIMIT]
|
156
|
+
end
|
157
|
+
|
177
158
|
def each_line
|
178
159
|
table.transpose.each {|line| yield line}
|
179
160
|
end
|
180
161
|
|
162
|
+
def to_a
|
163
|
+
table.to_a
|
164
|
+
end
|
165
|
+
|
181
166
|
def to_table
|
182
|
-
|
167
|
+
table.transpose
|
168
|
+
end
|
169
|
+
|
170
|
+
def [](index)
|
171
|
+
table[index]
|
172
|
+
end
|
173
|
+
|
174
|
+
def include_row?(row)
|
175
|
+
to_table.include?(row)
|
183
176
|
end
|
184
177
|
|
185
178
|
def == (otherDataResult)
|
@@ -275,7 +268,7 @@ module GoodData
|
|
275
268
|
(row_headers.size).times do |j|
|
276
269
|
table[headers_width + i] ||= []
|
277
270
|
# puts "[#{headers_width + i}, #{headers_height + j}] [#{i}][#{j}]=#{xtab_data[j][i]}"
|
278
|
-
table[headers_width + i][headers_height + j] = xtab_data[j][i]
|
271
|
+
table[headers_width + i][headers_height + j] = BigDecimal(xtab_data[j][i])
|
279
272
|
end
|
280
273
|
end
|
281
274
|
end
|