gooddata 0.6.0.pre11 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +12 -1
- data/.yardopts +2 -0
- data/README.md +6 -3
- data/Rakefile +24 -7
- data/gooddata +2 -2
- data/gooddata.gemspec +4 -3
- data/lib/gooddata.rb +17 -12
- data/lib/gooddata/bricks/base_downloader.rb +7 -7
- data/lib/gooddata/bricks/brick.rb +7 -8
- data/lib/gooddata/bricks/bricks.rb +4 -1
- data/lib/gooddata/bricks/middleware/base_middleware.rb +2 -2
- data/lib/gooddata/bricks/middleware/bench_middleware.rb +5 -6
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +21 -22
- data/lib/gooddata/bricks/middleware/fs_upload_middleware.rb +3 -4
- data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +14 -14
- data/lib/gooddata/bricks/middleware/logger_middleware.rb +6 -6
- data/lib/gooddata/bricks/middleware/middleware.rb +4 -1
- data/lib/gooddata/bricks/middleware/restforce_middleware.rb +29 -32
- data/lib/gooddata/bricks/middleware/stdout_middleware.rb +5 -5
- data/lib/gooddata/bricks/middleware/twitter_middleware.rb +6 -8
- data/lib/gooddata/bricks/utils.rb +3 -3
- data/lib/gooddata/cli/cli.rb +4 -2
- data/lib/gooddata/cli/commands/api_cmd.rb +6 -4
- data/lib/gooddata/cli/commands/auth_cmd.rb +5 -3
- data/lib/gooddata/cli/commands/console_cmd.rb +1 -1
- data/lib/gooddata/cli/commands/process_cmd.rb +6 -4
- data/lib/gooddata/cli/commands/profile_cmd.rb +5 -3
- data/lib/gooddata/cli/commands/project_cmd.rb +24 -22
- data/lib/gooddata/cli/commands/run_ruby_cmd.rb +12 -10
- data/lib/gooddata/cli/commands/scaffold_cmd.rb +8 -6
- data/lib/gooddata/cli/hooks.rb +4 -2
- data/lib/gooddata/cli/shared.rb +3 -1
- data/lib/gooddata/cli/terminal.rb +16 -0
- data/lib/gooddata/client.rb +28 -22
- data/lib/gooddata/commands/api.rb +43 -26
- data/lib/gooddata/commands/auth.rb +22 -53
- data/lib/gooddata/commands/base.rb +2 -0
- data/lib/gooddata/commands/commands.rb +3 -0
- data/lib/gooddata/commands/datasets.rb +39 -136
- data/lib/gooddata/commands/process.rb +134 -130
- data/lib/gooddata/commands/profile.rb +2 -0
- data/lib/gooddata/commands/projects.rb +91 -129
- data/lib/gooddata/commands/runners.rb +11 -11
- data/lib/gooddata/commands/scaffold.rb +28 -26
- data/lib/gooddata/connection.rb +61 -68
- data/lib/gooddata/core/core.rb +1 -2
- data/lib/gooddata/data/data.rb +7 -0
- data/lib/gooddata/data/guesser.rb +114 -0
- data/lib/gooddata/exceptions/command_failed.rb +7 -0
- data/lib/gooddata/exceptions/exceptions.rb +7 -0
- data/lib/gooddata/{exceptions.rb → exceptions/project_not_found.rb} +2 -2
- data/lib/gooddata/extensions/big_decimal.rb +5 -0
- data/lib/gooddata/extract.rb +2 -0
- data/lib/gooddata/goodzilla/goodzilla.rb +11 -12
- data/lib/gooddata/helpers.rb +49 -35
- data/lib/gooddata/models/attribute.rb +7 -5
- data/lib/gooddata/models/dashboard.rb +44 -45
- data/lib/gooddata/models/data_result.rb +10 -13
- data/lib/gooddata/models/data_set.rb +6 -6
- data/lib/gooddata/models/display_form.rb +4 -4
- data/lib/gooddata/models/empty_result.rb +4 -3
- data/lib/gooddata/models/fact.rb +5 -5
- data/lib/gooddata/models/links.rb +3 -1
- data/lib/gooddata/models/metadata.rb +34 -32
- data/lib/gooddata/models/metric.rb +33 -34
- data/lib/gooddata/models/model.rb +165 -173
- data/lib/gooddata/models/models.rb +3 -0
- data/lib/gooddata/models/process.rb +18 -17
- data/lib/gooddata/models/profile.rb +3 -1
- data/lib/gooddata/models/project.rb +107 -35
- data/lib/gooddata/models/project_metadata.rb +12 -12
- data/lib/gooddata/models/report.rb +31 -30
- data/lib/gooddata/models/report_data_result.rb +22 -19
- data/lib/gooddata/models/report_definition.rb +101 -80
- data/lib/gooddata/version.rb +5 -3
- data/lib/templates/bricks/brick.rb.erb +3 -3
- data/lib/templates/bricks/main.rb.erb +3 -2
- data/lib/templates/project/Goodfile.erb +2 -2
- data/lib/templates/project/model/model.rb.erb +19 -19
- data/spec/data/.gooddata +4 -0
- data/spec/helpers/blueprint_helper.rb +2 -2
- data/spec/helpers/cli_helper.rb +28 -0
- data/spec/helpers/connection_helper.rb +2 -2
- data/spec/integration/command_projects_spec.rb +1 -1
- data/spec/integration/create_from_template_spec.rb +12 -0
- data/spec/integration/full_project_spec.rb +2 -2
- data/spec/integration/partial_md_export_import_spec.rb +36 -0
- data/spec/logging_in_logging_out_spec.rb +1 -1
- data/spec/spec_helper.rb +29 -2
- data/spec/unit/cli/cli_spec.rb +3 -3
- data/spec/unit/cli/commands/cmd_api_spec.rb +21 -4
- data/spec/unit/cli/commands/cmd_auth_spec.rb +2 -4
- data/spec/unit/cli/commands/cmd_process_spec.rb +20 -4
- data/spec/unit/cli/commands/cmd_profile_spec.rb +9 -4
- data/spec/unit/cli/commands/cmd_project_spec.rb +53 -4
- data/spec/unit/cli/commands/cmd_run_ruby_spec.rb +2 -4
- data/spec/unit/cli/commands/cmd_scaffold_spec.rb +14 -4
- data/spec/unit/commands/command_api_spec.rb +21 -2
- data/spec/unit/commands/command_auth_spec.rb +62 -1
- data/spec/unit/commands/command_dataset_spec.rb +31 -3
- data/spec/unit/commands/command_process_spec.rb +75 -1
- data/spec/unit/commands/command_profile_spec.rb +7 -1
- data/spec/unit/commands/command_projects_spec.rb +1 -1
- data/spec/unit/commands/command_scaffold_spec.rb +46 -1
- data/spec/unit/core/connection_spec.rb +1 -0
- data/spec/unit/data/guesser_spec.rb +54 -0
- data/spec/unit/helpers_spec.rb +47 -0
- data/spec/unit/model/schema_builder_spec.rb +2 -0
- data/spec/unit/model/tools_spec.rb +89 -0
- data/test/test_upload.rb +39 -15
- metadata +98 -75
- data/test/test_commands.rb +0 -85
- data/test/test_guessing.rb +0 -46
- data/test/test_model.rb +0 -81
- data/test/test_rest_api_basic.rb +0 -41
@@ -1,8 +1,9 @@
|
|
1
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require_relative 'data_result.rb'
|
2
4
|
|
3
5
|
module GoodData
|
4
6
|
class EmptyResult < DataResult
|
5
|
-
|
6
7
|
def initialize(data, options = {})
|
7
8
|
super(data)
|
8
9
|
@options = options
|
@@ -10,7 +11,7 @@ module GoodData
|
|
10
11
|
end
|
11
12
|
|
12
13
|
def to_s
|
13
|
-
|
14
|
+
'No Data'
|
14
15
|
end
|
15
16
|
|
16
17
|
def assemble_table
|
data/lib/gooddata/models/fact.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
|
1
|
+
# encoding: UTF-8
|
2
2
|
|
3
|
-
|
4
|
-
class Fact < GoodData::MdObject
|
3
|
+
require_relative 'metadata'
|
5
4
|
|
5
|
+
module GoodData
|
6
|
+
class Fact < GoodData::MdObject
|
6
7
|
root_key :fact
|
7
8
|
|
8
9
|
class << self
|
9
10
|
def [](id)
|
10
11
|
if id == :all
|
11
12
|
GoodData.get(GoodData.project.md['query'] + '/facts/')['query']['entries']
|
12
|
-
else
|
13
|
+
else
|
13
14
|
super
|
14
15
|
end
|
15
16
|
end
|
16
17
|
end
|
17
|
-
|
18
18
|
end
|
19
19
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
module GoodData
|
2
4
|
class Links
|
3
5
|
attr_reader :data
|
@@ -8,7 +10,7 @@ module GoodData
|
|
8
10
|
category = item['category']
|
9
11
|
if @data[category] then
|
10
12
|
if @data[category]['category'] == category then
|
11
|
-
@data[category] = {
|
13
|
+
@data[category] = {@data[category]['identifier'] => @data[category]}
|
12
14
|
end
|
13
15
|
@data[category][item['identifier']] = item
|
14
16
|
else
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require_relative 'model'
|
2
4
|
|
3
5
|
module GoodData
|
4
6
|
class MdObject
|
@@ -7,25 +9,25 @@ module GoodData
|
|
7
9
|
|
8
10
|
class << self
|
9
11
|
def root_key(a_key)
|
10
|
-
define_method :root_key, Proc.new { a_key.to_s}
|
12
|
+
define_method :root_key, Proc.new { a_key.to_s }
|
11
13
|
end
|
12
|
-
|
14
|
+
|
13
15
|
def [](id)
|
14
16
|
raise "Cannot search for nil #{self.class}" unless id
|
15
17
|
uri = if id.is_a? Integer or id =~ /^\d+$/
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
"#{GoodData.project.md[MD_OBJ_CTG]}/#{id}"
|
19
|
+
elsif id !~ /\//
|
20
|
+
identifier_to_uri id
|
21
|
+
elsif id =~ /^\//
|
22
|
+
id
|
23
|
+
else
|
24
|
+
raise 'Unexpected object id format: expected numeric ID, identifier with no slashes or an URI starting with a slash'
|
25
|
+
end
|
24
26
|
self.new(GoodData.get uri) unless uri.nil?
|
25
27
|
end
|
26
28
|
|
27
29
|
def find_by_tag(tag)
|
28
|
-
self[:all].find_all {|r| r[
|
30
|
+
self[:all].find_all { |r| r['tags'].split(',').include?(tag) }
|
29
31
|
end
|
30
32
|
|
31
33
|
def get_by_id(id)
|
@@ -36,27 +38,26 @@ module GoodData
|
|
36
38
|
def find_first_by_title(title)
|
37
39
|
all = self[:all]
|
38
40
|
item = if title.is_a?(Regexp)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
self[item[
|
41
|
+
all.find { |r| r['title'] =~ title }
|
42
|
+
else
|
43
|
+
all.find { |r| r['title'] == title }
|
44
|
+
end
|
45
|
+
self[item['link']] unless item.nil?
|
44
46
|
end
|
45
47
|
|
46
48
|
def identifier_to_uri(*ids)
|
47
|
-
raise NoProjectError.new
|
48
|
-
uri
|
49
|
-
response = GoodData.post uri, {
|
49
|
+
raise NoProjectError.new 'Connect to a project before searching for an object' unless GoodData.project
|
50
|
+
uri = GoodData.project.md[IDENTIFIERS_CFG]
|
51
|
+
response = GoodData.post uri, {'identifierToUri' => ids}
|
50
52
|
if response['identifiers'].empty?
|
51
53
|
nil
|
52
54
|
else
|
53
|
-
ids = response['identifiers'].map {|x| x['uri']}
|
55
|
+
ids = response['identifiers'].map { |x| x['uri'] }
|
54
56
|
ids.count == 1 ? ids.first : ids
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
alias :id_to_uri :identifier_to_uri
|
59
|
-
|
60
61
|
end
|
61
62
|
|
62
63
|
def initialize(json)
|
@@ -66,7 +67,7 @@ module GoodData
|
|
66
67
|
def delete
|
67
68
|
if saved?
|
68
69
|
GoodData.delete(uri)
|
69
|
-
meta.delete(
|
70
|
+
meta.delete('uri')
|
70
71
|
# ["uri"] = nil
|
71
72
|
end
|
72
73
|
end
|
@@ -107,19 +108,19 @@ module GoodData
|
|
107
108
|
end
|
108
109
|
|
109
110
|
def title=(a_title)
|
110
|
-
data[
|
111
|
+
data['meta']['title'] = a_title
|
111
112
|
end
|
112
113
|
|
113
114
|
def summary=(a_summary)
|
114
|
-
data[
|
115
|
+
data['meta']['summary'] = a_summary
|
115
116
|
end
|
116
117
|
|
117
118
|
def tags
|
118
|
-
data[
|
119
|
+
data['meta']['tags']
|
119
120
|
end
|
120
121
|
|
121
122
|
def tags=(list_of_tags)
|
122
|
-
data[
|
123
|
+
data['meta']['tags'] = list_of_tags
|
123
124
|
end
|
124
125
|
|
125
126
|
def meta
|
@@ -136,12 +137,12 @@ module GoodData
|
|
136
137
|
|
137
138
|
def get_usedby
|
138
139
|
result = GoodData.get "#{GoodData.project.md['usedby2']}/#{obj_id}"
|
139
|
-
result[
|
140
|
+
result['entries']
|
140
141
|
end
|
141
142
|
|
142
143
|
def get_using
|
143
144
|
result = GoodData.get "#{GoodData.project.md['using2']}/#{obj_id}"
|
144
|
-
result[
|
145
|
+
result['entries']
|
145
146
|
end
|
146
147
|
|
147
148
|
def to_json
|
@@ -153,7 +154,8 @@ module GoodData
|
|
153
154
|
end
|
154
155
|
|
155
156
|
def data
|
156
|
-
raw_data
|
157
|
+
key = methods.include?(:root_key) ? root_key : raw_data.keys.first
|
158
|
+
raw_data[key]
|
157
159
|
end
|
158
160
|
|
159
161
|
def saved?
|
@@ -161,13 +163,13 @@ module GoodData
|
|
161
163
|
end
|
162
164
|
|
163
165
|
def save
|
164
|
-
fail(
|
166
|
+
fail('Validation failed') unless validate
|
165
167
|
|
166
168
|
if saved?
|
167
169
|
GoodData.put(uri, to_json)
|
168
170
|
else
|
169
171
|
result = GoodData.post(GoodData.project.md['obj'], to_json)
|
170
|
-
saved_object = self.class[result[
|
172
|
+
saved_object = self.class[result['uri']]
|
171
173
|
@json = saved_object.raw_data
|
172
174
|
end
|
173
175
|
self
|
@@ -1,17 +1,18 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require_relative 'metadata'
|
4
|
+
require_relative '../goodzilla/goodzilla'
|
3
5
|
|
4
6
|
module GoodData
|
5
7
|
# Metric representation
|
6
8
|
class Metric < GoodData::MdObject
|
7
|
-
|
8
9
|
root_key :metric
|
9
10
|
|
10
11
|
class << self
|
11
12
|
def [](id)
|
12
13
|
if id == :all
|
13
14
|
GoodData.get(GoodData.project.md['query'] + '/metrics/')['query']['entries']
|
14
|
-
else
|
15
|
+
else
|
15
16
|
super
|
16
17
|
end
|
17
18
|
end
|
@@ -32,43 +33,43 @@ module GoodData
|
|
32
33
|
else
|
33
34
|
title = options[:title]
|
34
35
|
summary = options[:summary]
|
35
|
-
expression = options[:expression] || fail(
|
36
|
+
expression = options[:expression] || fail('Metric has to have its expression defined')
|
36
37
|
extended_notation = options[:extended_notation] || false
|
37
38
|
end
|
38
39
|
|
39
40
|
expression = if extended_notation
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
41
|
+
dict = {
|
42
|
+
:facts => GoodData::Fact[:all].reduce({}) { |memo, item| memo[item['title']] = item['link']; memo },
|
43
|
+
:attributes => GoodData::Attribute[:all].reduce({}) { |memo, item| memo[item['title']] = item['link']; memo },
|
44
|
+
:metrics => GoodData::Metric[:all].reduce({}) { |memo, item| memo[item['title']] = item['link']; memo },
|
45
|
+
}
|
46
|
+
interpolated_metric = GoodData::SmallGoodZilla.interpolate_metric(expression, dict)
|
47
|
+
interpolated_metric
|
48
|
+
else
|
49
|
+
expression
|
50
|
+
end
|
50
51
|
|
51
52
|
Metric.new({
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
53
|
+
'metric' => {
|
54
|
+
'content' => {
|
55
|
+
'format' => '#,##0',
|
56
|
+
'expression' => expression
|
57
|
+
},
|
58
|
+
'meta' => {
|
59
|
+
'tags' => '',
|
60
|
+
'summary' => summary,
|
61
|
+
'title' => title,
|
62
|
+
}
|
63
|
+
}
|
64
|
+
})
|
64
65
|
end
|
65
66
|
|
66
67
|
def execute(expression, options={})
|
67
68
|
m = if expression.is_a?(String)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
GoodData::Metric.create({:title => 'Temporary metric to be deleted', :expression => expression}.merge(options))
|
70
|
+
else
|
71
|
+
GoodData::Metric.create({:title => 'Temporary metric to be deleted'}.merge(expression))
|
72
|
+
end
|
72
73
|
m.execute
|
73
74
|
end
|
74
75
|
|
@@ -79,7 +80,6 @@ module GoodData
|
|
79
80
|
execute(expression.merge({:extended_notation => true}))
|
80
81
|
end
|
81
82
|
end
|
82
|
-
|
83
83
|
end
|
84
84
|
|
85
85
|
def execute
|
@@ -88,13 +88,12 @@ module GoodData
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def validate
|
91
|
-
fail
|
91
|
+
fail 'Meric needs to have title' if title.nil?
|
92
92
|
true
|
93
93
|
end
|
94
94
|
|
95
95
|
def is_metric?
|
96
96
|
true
|
97
97
|
end
|
98
|
-
|
99
98
|
end
|
100
99
|
end
|
@@ -1,13 +1,16 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require_relative '../helpers'
|
4
|
+
|
1
5
|
require 'open-uri'
|
2
6
|
require 'active_support/all'
|
7
|
+
|
3
8
|
##
|
4
9
|
# Module containing classes that counter-part GoodData server-side meta-data
|
5
10
|
# elements, including the server-side data model.
|
6
11
|
#
|
7
12
|
module GoodData
|
8
|
-
|
9
13
|
module Model
|
10
|
-
|
11
14
|
# GoodData REST API categories
|
12
15
|
LDM_CTG = 'ldm'
|
13
16
|
LDM_MANAGE_CTG = 'ldm-manage'
|
@@ -24,7 +27,7 @@ module GoodData
|
|
24
27
|
LABEL_PREFIX = 'label'
|
25
28
|
FACT_PREFIX = 'fact'
|
26
29
|
DATE_FACT_PREFIX = 'dt'
|
27
|
-
DATE_ATTRIBUTE =
|
30
|
+
DATE_ATTRIBUTE = 'date'
|
28
31
|
DATE_ATTRIBUTE_DEFAULT_DISPLAY_FORM = 'mdyy'
|
29
32
|
TIME_FACT_PREFIX = 'tm.dt'
|
30
33
|
TIME_ATTRIBUTE_PREFIX = 'attr.time'
|
@@ -83,13 +86,13 @@ module GoodData
|
|
83
86
|
pull = {'pullIntegration' => File.basename(dir)}
|
84
87
|
link = project.md.links('etl')['pull']
|
85
88
|
task = GoodData.post link, pull
|
86
|
-
while
|
89
|
+
while GoodData.get(task['pullTask']['uri'])['taskStatus'] === 'RUNNING' || GoodData.get(task['pullTask']['uri'])['taskStatus'] === 'PREPARED'
|
87
90
|
sleep 30
|
88
91
|
end
|
89
|
-
if
|
92
|
+
if GoodData.get(task['pullTask']['uri'])['taskStatus'] == 'ERROR'
|
90
93
|
s = StringIO.new
|
91
94
|
GoodData.download_from_user_webdav(File.basename(dir) + '/upload_status.json', s)
|
92
|
-
js =
|
95
|
+
js = MultiJson.load(s.string)
|
93
96
|
fail "Load Failed with error #{JSON.pretty_generate(js)}"
|
94
97
|
end
|
95
98
|
end
|
@@ -100,20 +103,18 @@ module GoodData
|
|
100
103
|
d = Marshal.load(Marshal.dump(a_schema_blueprint))
|
101
104
|
d[:columns] = d[:columns] + b_schema_blueprint[:columns]
|
102
105
|
d[:columns].uniq!
|
103
|
-
columns_that_failed_to_merge = d[:columns].group_by {|x| x[:name]}.map {|k, v| [k, v.count]}.find_all {|x| x[1] > 1}
|
106
|
+
columns_that_failed_to_merge = d[:columns].group_by { |x| x[:name] }.map { |k, v| [k, v.count] }.find_all { |x| x[1] > 1 }
|
104
107
|
fail "Columns #{columns_that_failed_to_merge} failed to merge. When merging columns with the same name they have to be identical." unless columns_that_failed_to_merge.empty?
|
105
108
|
d
|
106
109
|
end
|
107
|
-
|
108
110
|
end
|
109
111
|
|
110
112
|
class ProjectBlueprint
|
111
|
-
|
112
113
|
attr_accessor :data
|
113
114
|
|
114
115
|
def self.from_json(spec)
|
115
116
|
if spec.is_a?(String)
|
116
|
-
ProjectBlueprint.new(
|
117
|
+
ProjectBlueprint.new(MultiJson.load(File.read(spec), :symbolize_keys => true))
|
117
118
|
else
|
118
119
|
ProjectBlueprint.new(spec)
|
119
120
|
end
|
@@ -140,7 +141,7 @@ module GoodData
|
|
140
141
|
end
|
141
142
|
|
142
143
|
def remove_dataset(dataset_name)
|
143
|
-
x = data[:datasets].find {|d| d[:name] == dataset_name}
|
144
|
+
x = data[:datasets].find { |d| d[:name] == dataset_name }
|
144
145
|
index = data[:datasets].index(x)
|
145
146
|
data[:datasets].delete_at(index)
|
146
147
|
end
|
@@ -205,28 +206,26 @@ module GoodData
|
|
205
206
|
|
206
207
|
def to_wire_model
|
207
208
|
{
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
209
|
+
'diffRequest' => {
|
210
|
+
'targetModel' => {
|
211
|
+
'projectModel' => {
|
212
|
+
'datasets' => datasets.map { |d| d.to_wire_model },
|
213
|
+
'dateDimensions' => date_dimensions.map { |d|
|
214
|
+
{
|
215
|
+
'dateDimension' => {
|
216
|
+
'name' => d[:name],
|
217
|
+
'title' => d[:title] || d[:name].humanize
|
218
|
+
}
|
219
|
+
} }
|
220
|
+
}}}}
|
220
221
|
end
|
221
222
|
|
222
223
|
def to_hash
|
223
224
|
@data
|
224
225
|
end
|
225
|
-
|
226
226
|
end
|
227
227
|
|
228
228
|
class SchemaBlueprint
|
229
|
-
|
230
229
|
attr_accessor :data
|
231
230
|
|
232
231
|
def change(&block)
|
@@ -243,8 +242,8 @@ module GoodData
|
|
243
242
|
|
244
243
|
def upload(source, options={})
|
245
244
|
project = options[:project] || GoodData.project
|
246
|
-
fail
|
247
|
-
mode = options[:load] ||
|
245
|
+
fail 'You have to specify a project into which you want to load.' if project.nil?
|
246
|
+
mode = options[:load] || 'FULL'
|
248
247
|
project.upload(source, to_schema, mode)
|
249
248
|
end
|
250
249
|
|
@@ -271,7 +270,7 @@ module GoodData
|
|
271
270
|
end
|
272
271
|
|
273
272
|
def has_anchor?
|
274
|
-
columns.any? { |c| c[:type].to_s ==
|
273
|
+
columns.any? { |c| c[:type].to_s == 'anchor' }
|
275
274
|
end
|
276
275
|
|
277
276
|
def anchor
|
@@ -335,17 +334,14 @@ module GoodData
|
|
335
334
|
def ==(other)
|
336
335
|
to_hash == other.to_hash
|
337
336
|
end
|
338
|
-
|
339
337
|
end
|
340
338
|
|
341
339
|
class ProjectBuilder
|
342
|
-
|
343
340
|
attr_reader :title, :datasets, :reports, :metrics, :uploads, :users, :assert_report, :date_dimensions
|
344
341
|
|
345
342
|
class << self
|
346
|
-
|
347
|
-
|
348
|
-
pb = ProjectBuilder.new
|
343
|
+
def create_from_data(blueprint, title = 'Title')
|
344
|
+
pb = ProjectBuilder.new(title)
|
349
345
|
pb.data = blueprint.to_hash
|
350
346
|
pb
|
351
347
|
end
|
@@ -355,7 +351,6 @@ module GoodData
|
|
355
351
|
block.call(pb)
|
356
352
|
pb
|
357
353
|
end
|
358
|
-
|
359
354
|
end
|
360
355
|
|
361
356
|
def initialize(title)
|
@@ -370,8 +365,14 @@ module GoodData
|
|
370
365
|
@date_dimensions = []
|
371
366
|
end
|
372
367
|
|
373
|
-
def add_date_dimension(name, options={})
|
374
|
-
|
368
|
+
def add_date_dimension(name, options = {})
|
369
|
+
dimension = {
|
370
|
+
urn: options[:urn],
|
371
|
+
name: name,
|
372
|
+
title: options[:title]
|
373
|
+
}
|
374
|
+
|
375
|
+
@date_dimensions << dimension
|
375
376
|
end
|
376
377
|
|
377
378
|
def add_dataset(name, &block)
|
@@ -403,12 +404,12 @@ module GoodData
|
|
403
404
|
end
|
404
405
|
|
405
406
|
def load_metrics(file)
|
406
|
-
new_metrics =
|
407
|
+
new_metrics = MultiJson.load(open(file).read, :symbolize_keys => true)
|
407
408
|
@metrics = @metrics + new_metrics
|
408
409
|
end
|
409
410
|
|
410
411
|
def load_datasets(file)
|
411
|
-
new_metrics =
|
412
|
+
new_metrics = MultiJson.load(open(file).read, :symbolize_keys => true)
|
412
413
|
@datasets = @datasets + new_metrics
|
413
414
|
end
|
414
415
|
|
@@ -417,12 +418,12 @@ module GoodData
|
|
417
418
|
end
|
418
419
|
|
419
420
|
def upload(data, options={})
|
420
|
-
mode = options[:mode] ||
|
421
|
+
mode = options[:mode] || 'FULL'
|
421
422
|
dataset = options[:dataset]
|
422
423
|
@uploads << {
|
423
|
-
|
424
|
-
|
425
|
-
|
424
|
+
:source => data,
|
425
|
+
:mode => mode,
|
426
|
+
:dataset => dataset
|
426
427
|
}
|
427
428
|
end
|
428
429
|
|
@@ -442,26 +443,24 @@ module GoodData
|
|
442
443
|
|
443
444
|
def to_hash
|
444
445
|
{
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
446
|
+
:title => @title,
|
447
|
+
:datasets => @datasets,
|
448
|
+
:uploads => @uploads,
|
449
|
+
:dashboards => @dashboards,
|
450
|
+
:metrics => @metrics,
|
451
|
+
:reports => @reports,
|
452
|
+
:users => @users,
|
453
|
+
:assert_tests => @assert_tests,
|
454
|
+
:date_dimensions => @date_dimensions
|
454
455
|
}
|
455
456
|
end
|
456
457
|
|
457
458
|
def get_dataset(name)
|
458
459
|
datasets.find { |d| d.name == name }
|
459
460
|
end
|
460
|
-
|
461
461
|
end
|
462
462
|
|
463
463
|
class DashboardBuilder
|
464
|
-
|
465
464
|
def initialize(title)
|
466
465
|
@title = title
|
467
466
|
@tabs = []
|
@@ -476,14 +475,13 @@ module GoodData
|
|
476
475
|
|
477
476
|
def to_hash
|
478
477
|
{
|
479
|
-
|
480
|
-
|
478
|
+
:name => @name,
|
479
|
+
:tabs => @tabs.map { |tab| tab.to_hash }
|
481
480
|
}
|
482
481
|
end
|
483
482
|
end
|
484
483
|
|
485
484
|
class TabBuilder
|
486
|
-
|
487
485
|
def initialize(title)
|
488
486
|
@title = title
|
489
487
|
@stuff = []
|
@@ -495,31 +493,27 @@ module GoodData
|
|
495
493
|
|
496
494
|
def to_hash
|
497
495
|
{
|
498
|
-
|
499
|
-
|
496
|
+
:title => @title,
|
497
|
+
:items => @stuff
|
500
498
|
}
|
501
499
|
end
|
502
|
-
|
503
500
|
end
|
504
501
|
|
505
502
|
class SchemaBuilder
|
506
|
-
|
507
503
|
attr_accessor :data
|
508
504
|
|
509
505
|
class << self
|
510
|
-
|
511
506
|
def create_from_data(blueprint)
|
512
507
|
sc = SchemaBuilder.new
|
513
508
|
sc.data = blueprint.to_hash
|
514
509
|
sc
|
515
510
|
end
|
516
|
-
|
517
511
|
end
|
518
512
|
|
519
513
|
def initialize(name=nil)
|
520
514
|
@data = {
|
521
|
-
|
522
|
-
|
515
|
+
:name => name,
|
516
|
+
:columns => []
|
523
517
|
}
|
524
518
|
end
|
525
519
|
|
@@ -575,20 +569,17 @@ module GoodData
|
|
575
569
|
def to_schema
|
576
570
|
Schema.new(to_hash)
|
577
571
|
end
|
578
|
-
|
579
572
|
end
|
580
573
|
|
581
574
|
class ProjectCreator
|
582
|
-
|
583
575
|
class << self
|
584
576
|
def migrate(options={})
|
585
|
-
|
586
|
-
spec = options[:spec] || fail("You need to provide spec for migration")
|
577
|
+
spec = options[:spec] || fail('You need to provide spec for migration')
|
587
578
|
spec = spec.to_hash
|
588
579
|
|
589
580
|
token = options[:token]
|
590
581
|
project = options[:project] || GoodData::Project.create(:title => spec[:title], :auth_token => token)
|
591
|
-
fail(
|
582
|
+
fail('You need to specify token for project creation') if token.nil? && project.nil?
|
592
583
|
|
593
584
|
begin
|
594
585
|
GoodData.with_project(project) do |p|
|
@@ -615,8 +606,9 @@ module GoodData
|
|
615
606
|
bp = ProjectBlueprint.new(spec)
|
616
607
|
# schema = Schema.load(schema) unless schema.respond_to?(:to_maql_create)
|
617
608
|
# project = GoodData.project unless project
|
618
|
-
|
619
|
-
|
609
|
+
uri = "/gdc/projects/#{GoodData.project.pid}/model/diff"
|
610
|
+
result = GoodData.post(uri, bp.to_wire_model)
|
611
|
+
link = result['asyncTask']['link']['poll']
|
620
612
|
response = GoodData.get(link, :process => false)
|
621
613
|
# pp response
|
622
614
|
while response.code != 200
|
@@ -630,9 +622,9 @@ module GoodData
|
|
630
622
|
response = GoodData.get(link)
|
631
623
|
ldm_links = GoodData.get project.md[LDM_CTG]
|
632
624
|
ldm_uri = Links.new(ldm_links)[LDM_MANAGE_CTG]
|
633
|
-
chunks = response[
|
625
|
+
chunks = response['projectModelDiff']['updateScripts'].find_all { |script| script['updateScript']['preserveData'] == true && script['updateScript']['cascadeDrops'] == false }.map { |x| x['updateScript']['maqlDdlChunks'] }.flatten
|
634
626
|
chunks.each do |chunk|
|
635
|
-
GoodData.post ldm_uri, {
|
627
|
+
GoodData.post ldm_uri, {'manage' => {'maql' => chunk}}
|
636
628
|
end
|
637
629
|
|
638
630
|
bp.datasets.each do |ds|
|
@@ -669,7 +661,7 @@ module GoodData
|
|
669
661
|
def load(project, spec)
|
670
662
|
if spec.has_key?(:uploads)
|
671
663
|
spec[:uploads].each do |load|
|
672
|
-
schema = GoodData::Model::Schema.new(spec[:datasets].detect {|d| d[:name] == load[:dataset]})
|
664
|
+
schema = GoodData::Model::Schema.new(spec[:datasets].detect { |d| d[:name] == load[:dataset] })
|
673
665
|
project.upload(load[:source], schema, load[:mode])
|
674
666
|
end
|
675
667
|
end
|
@@ -716,20 +708,23 @@ module GoodData
|
|
716
708
|
Schema.new JSON.load(open(file))
|
717
709
|
end
|
718
710
|
|
719
|
-
def initialize(config, name =
|
711
|
+
def initialize(config, name = 'Default Name', title = 'Default Title')
|
720
712
|
super()
|
721
713
|
@fields = []
|
722
714
|
@attributes = []
|
723
715
|
@facts = []
|
724
716
|
@folders = {
|
725
|
-
|
726
|
-
|
717
|
+
:facts => {},
|
718
|
+
:attributes => {}
|
727
719
|
}
|
728
720
|
@references = []
|
729
721
|
@labels = []
|
730
722
|
|
731
723
|
config[:name] = name unless config[:name]
|
732
|
-
config[:title] = config[:
|
724
|
+
config[:title] = config[:name] unless config[:title]
|
725
|
+
config[:title] = title unless config[:title]
|
726
|
+
config[:title] = config[:title].humanize
|
727
|
+
|
733
728
|
fail 'Schema name not specified' unless config[:name]
|
734
729
|
self.name = config[:name]
|
735
730
|
self.title = config[:title]
|
@@ -739,17 +734,17 @@ module GoodData
|
|
739
734
|
def config=(config)
|
740
735
|
config[:columns].each do |c|
|
741
736
|
case c[:type].to_s
|
742
|
-
when
|
737
|
+
when 'attribute'
|
743
738
|
add_attribute c
|
744
|
-
when
|
739
|
+
when 'fact'
|
745
740
|
add_fact c
|
746
|
-
when
|
741
|
+
when 'date'
|
747
742
|
add_date c
|
748
|
-
when
|
743
|
+
when 'anchor'
|
749
744
|
set_anchor c
|
750
|
-
when
|
745
|
+
when 'label'
|
751
746
|
add_label c
|
752
|
-
when
|
747
|
+
when 'reference'
|
753
748
|
add_reference c
|
754
749
|
else
|
755
750
|
fail "Unexpected type #{c[:type]} in #{c.inspect}"
|
@@ -773,7 +768,7 @@ module GoodData
|
|
773
768
|
# Generates MAQL DDL script to drop this data set and included pieces
|
774
769
|
#
|
775
770
|
def to_maql_drop
|
776
|
-
maql =
|
771
|
+
maql = ''
|
777
772
|
[attributes, facts].each do |obj|
|
778
773
|
maql += obj.to_maql_drop
|
779
774
|
end
|
@@ -784,9 +779,10 @@ module GoodData
|
|
784
779
|
# Generates MAQL DDL script to create this data set and included pieces
|
785
780
|
#
|
786
781
|
def to_maql_create
|
782
|
+
# TODO: Use template (.erb)
|
787
783
|
maql = "# Create the '#{self.title}' data set\n"
|
788
784
|
maql += "CREATE DATASET {#{self.identifier}} VISUAL (TITLE \"#{self.title}\");\n\n"
|
789
|
-
[
|
785
|
+
[attributes, facts, {1 => @anchor}].each do |objects|
|
790
786
|
objects.values.each do |obj|
|
791
787
|
maql += "# Create '#{obj.title}' and add it to the '#{self.title}' data set.\n"
|
792
788
|
maql += obj.to_maql_create
|
@@ -809,7 +805,7 @@ module GoodData
|
|
809
805
|
folders_maql + "\n" + maql + "SYNCHRONIZE {#{identifier}};\n"
|
810
806
|
end
|
811
807
|
|
812
|
-
def upload(path, project = nil, mode =
|
808
|
+
def upload(path, project = nil, mode = 'FULL')
|
813
809
|
if path =~ URI::regexp
|
814
810
|
Tempfile.open('remote_file') do |temp|
|
815
811
|
temp << open(path).read
|
@@ -827,31 +823,32 @@ module GoodData
|
|
827
823
|
|
828
824
|
# Generates the SLI manifest describing the data loading
|
829
825
|
#
|
830
|
-
def to_manifest(mode=
|
826
|
+
def to_manifest(mode = 'FULL')
|
831
827
|
{
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
}
|
828
|
+
'dataSetSLIManifest' => {
|
829
|
+
'parts' => fields.reduce([]) { |memo, f| val = f.to_manifest_part(mode); memo << val unless val.nil?; memo },
|
830
|
+
'dataSet' => self.identifier,
|
831
|
+
'file' => 'data.csv', # should be configurable
|
832
|
+
'csvParams' => {
|
833
|
+
'quoteChar' => '"',
|
834
|
+
'escapeChar' => '"',
|
835
|
+
'separatorChar' => ',',
|
836
|
+
'endOfLine' => "\n"
|
842
837
|
}
|
838
|
+
}
|
843
839
|
}
|
844
840
|
end
|
845
841
|
|
846
842
|
def to_wire_model
|
847
843
|
{
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
844
|
+
'dataset' => {
|
845
|
+
'identifier' => identifier,
|
846
|
+
'title' => title,
|
847
|
+
'anchor' => @anchor.to_wire_model,
|
848
|
+
'facts' => facts.map { |f| f.to_wire_model },
|
849
|
+
'attributes' => attributes.map { |a| a.to_wire_model },
|
850
|
+
'references' => references.map { |r| r.is_a?(DateReference) ? r.schema_ref : type_prefix + '.' + r.schema_ref }}
|
851
|
+
}
|
855
852
|
end
|
856
853
|
|
857
854
|
private
|
@@ -902,14 +899,13 @@ module GoodData
|
|
902
899
|
date.parts.values.each { |p| @fields << p }
|
903
900
|
date.facts.each { |f| facts << f }
|
904
901
|
date.attributes.each { |a| attributes << a }
|
905
|
-
date.references.each {|r| references << r}
|
902
|
+
date.references.each { |r| references << r }
|
906
903
|
end
|
907
904
|
|
908
905
|
def set_anchor(column)
|
909
906
|
@anchor = Anchor.new column, self
|
910
907
|
@fields << @anchor
|
911
908
|
end
|
912
|
-
|
913
909
|
end
|
914
910
|
|
915
911
|
##
|
@@ -922,7 +918,9 @@ module GoodData
|
|
922
918
|
def initialize(hash, schema)
|
923
919
|
super()
|
924
920
|
raise ArgumentError.new("Schema must be provided, got #{schema.class}") unless schema.is_a? Schema
|
925
|
-
|
921
|
+
raise('Data set fields must have their names defined') if hash[:name].nil?
|
922
|
+
|
923
|
+
@name = hash[:name]
|
926
924
|
@title = hash[:title] || hash[:name].humanize
|
927
925
|
@folder = hash[:folder]
|
928
926
|
@schema = schema
|
@@ -985,7 +983,7 @@ module GoodData
|
|
985
983
|
end
|
986
984
|
|
987
985
|
def table
|
988
|
-
@table ||=
|
986
|
+
@table ||= 'd_' + @schema.name + '_' + name
|
989
987
|
end
|
990
988
|
|
991
989
|
def key;
|
@@ -1001,31 +999,30 @@ module GoodData
|
|
1001
999
|
|
1002
1000
|
def to_manifest_part(mode)
|
1003
1001
|
{
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1002
|
+
'referenceKey' => 1,
|
1003
|
+
'populates' => [@primary_label.identifier],
|
1004
|
+
'mode' => mode,
|
1005
|
+
'columnName' => name
|
1008
1006
|
}
|
1009
1007
|
end
|
1010
1008
|
|
1011
1009
|
def to_wire_model
|
1012
1010
|
{
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1011
|
+
'attribute' => {
|
1012
|
+
'identifier' => identifier,
|
1013
|
+
'title' => title,
|
1014
|
+
'labels' => labels.map do |l|
|
1015
|
+
{
|
1016
|
+
'label' => {
|
1017
|
+
'identifier' => l.identifier,
|
1018
|
+
'title' => l.title,
|
1019
|
+
'type' => 'GDC.text'
|
1020
|
+
}
|
1021
|
+
}
|
1022
|
+
end
|
1023
|
+
}
|
1026
1024
|
}
|
1027
1025
|
end
|
1028
|
-
|
1029
1026
|
end
|
1030
1027
|
|
1031
1028
|
##
|
@@ -1034,30 +1031,31 @@ module GoodData
|
|
1034
1031
|
# field
|
1035
1032
|
#
|
1036
1033
|
class Label < Column
|
1037
|
-
|
1038
1034
|
attr_accessor :attribute
|
1039
1035
|
|
1040
|
-
def type_prefix
|
1036
|
+
def type_prefix;
|
1037
|
+
'label';
|
1038
|
+
end
|
1041
1039
|
|
1042
1040
|
# def initialize(hash, schema)
|
1043
1041
|
def initialize(hash, attribute, schema)
|
1044
1042
|
super hash, schema
|
1045
|
-
attribute = attribute.nil? ? schema.fields.find {|field| field.name === hash[:reference]} : attribute
|
1043
|
+
attribute = attribute.nil? ? schema.fields.find { |field| field.name === hash[:reference] } : attribute
|
1046
1044
|
@attribute = attribute
|
1047
1045
|
attribute.labels << self
|
1048
1046
|
end
|
1049
1047
|
|
1050
1048
|
def to_maql_create
|
1051
|
-
|
1049
|
+
'# LABEL FROM LABEL'
|
1052
1050
|
"ALTER ATTRIBUTE {#{@attribute.identifier}} ADD LABELS {#{identifier}}" \
|
1053
1051
|
+ " VISUAL (TITLE #{title.inspect}) AS {#{column}};\n"
|
1054
1052
|
end
|
1055
1053
|
|
1056
1054
|
def to_manifest_part(mode)
|
1057
1055
|
{
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1056
|
+
'populates' => [identifier],
|
1057
|
+
'mode' => mode,
|
1058
|
+
'columnName' => name
|
1061
1059
|
}
|
1062
1060
|
end
|
1063
1061
|
|
@@ -1068,7 +1066,7 @@ module GoodData
|
|
1068
1066
|
alias :inspect_orig :inspect
|
1069
1067
|
|
1070
1068
|
def inspect
|
1071
|
-
inspect_orig.sub(/>$/, " @attribute
|
1069
|
+
inspect_orig.sub(/>$/, " @attribute=#{@attribute.to_s.sub(/>$/, " @name=#{@attribute.name}")}>")
|
1072
1070
|
end
|
1073
1071
|
end
|
1074
1072
|
|
@@ -1081,14 +1079,14 @@ module GoodData
|
|
1081
1079
|
if column then
|
1082
1080
|
super
|
1083
1081
|
else
|
1084
|
-
super({:type =>
|
1082
|
+
super({:type => 'anchor', :name => 'id'}, schema)
|
1085
1083
|
@labels = []
|
1086
1084
|
@primary_label = nil
|
1087
1085
|
end
|
1088
1086
|
end
|
1089
1087
|
|
1090
1088
|
def table
|
1091
|
-
@table ||=
|
1089
|
+
@table ||= 'f_' + @schema.name
|
1092
1090
|
end
|
1093
1091
|
|
1094
1092
|
def to_maql_create
|
@@ -1100,7 +1098,6 @@ module GoodData
|
|
1100
1098
|
end
|
1101
1099
|
maql
|
1102
1100
|
end
|
1103
|
-
|
1104
1101
|
end
|
1105
1102
|
|
1106
1103
|
##
|
@@ -1134,17 +1131,17 @@ module GoodData
|
|
1134
1131
|
|
1135
1132
|
def to_manifest_part(mode)
|
1136
1133
|
{
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1134
|
+
'populates' => [identifier],
|
1135
|
+
'mode' => mode,
|
1136
|
+
'columnName' => name
|
1140
1137
|
}
|
1141
1138
|
end
|
1142
1139
|
|
1143
1140
|
def to_wire_model
|
1144
1141
|
{
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1142
|
+
'fact' => {
|
1143
|
+
'identifier' => identifier,
|
1144
|
+
'title' => title
|
1148
1145
|
}
|
1149
1146
|
}
|
1150
1147
|
end
|
@@ -1154,7 +1151,6 @@ module GoodData
|
|
1154
1151
|
# Reference to another data set
|
1155
1152
|
#
|
1156
1153
|
class Reference < Column
|
1157
|
-
|
1158
1154
|
attr_accessor :reference, :schema_ref
|
1159
1155
|
|
1160
1156
|
def initialize(column, schema)
|
@@ -1193,10 +1189,10 @@ module GoodData
|
|
1193
1189
|
|
1194
1190
|
def to_manifest_part(mode)
|
1195
1191
|
{
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1192
|
+
'populates' => [label_column],
|
1193
|
+
'mode' => mode,
|
1194
|
+
'columnName' => name,
|
1195
|
+
'referenceKey' => 1
|
1200
1196
|
}
|
1201
1197
|
end
|
1202
1198
|
end
|
@@ -1205,14 +1201,13 @@ module GoodData
|
|
1205
1201
|
# Date as a reference to a date dimension
|
1206
1202
|
#
|
1207
1203
|
class DateReference < Reference
|
1208
|
-
|
1209
1204
|
attr_accessor :format, :output_format, :urn
|
1210
1205
|
|
1211
1206
|
def initialize(column, schema)
|
1212
1207
|
super column, schema
|
1213
|
-
@output_format = column[
|
1208
|
+
@output_format = column['format'] || 'dd/MM/yyyy'
|
1214
1209
|
@format = @output_format.gsub('yyyy', '%Y').gsub('MM', '%m').gsub('dd', '%d')
|
1215
|
-
@urn = column[:urn] ||
|
1210
|
+
@urn = column[:urn] || 'URN:GOODDATA:DATE'
|
1216
1211
|
end
|
1217
1212
|
|
1218
1213
|
def identifier
|
@@ -1221,11 +1216,11 @@ module GoodData
|
|
1221
1216
|
|
1222
1217
|
def to_manifest_part(mode)
|
1223
1218
|
{
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1219
|
+
'populates' => ["#{identifier}.#{DATE_ATTRIBUTE_DEFAULT_DISPLAY_FORM}"],
|
1220
|
+
'mode' => mode,
|
1221
|
+
'constraints' => {'date' => output_format},
|
1222
|
+
'columnName' => name,
|
1223
|
+
'referenceKey' => 1
|
1229
1224
|
}
|
1230
1225
|
end
|
1231
1226
|
|
@@ -1249,10 +1244,10 @@ module GoodData
|
|
1249
1244
|
|
1250
1245
|
def to_manifest_part(mode)
|
1251
1246
|
{
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1247
|
+
'populates' => ['label.stuff.mmddyy'],
|
1248
|
+
'format' => 'unknown',
|
1249
|
+
'mode' => mode,
|
1250
|
+
'referenceKey' => 1
|
1256
1251
|
}
|
1257
1252
|
end
|
1258
1253
|
end
|
@@ -1274,7 +1269,6 @@ module GoodData
|
|
1274
1269
|
# Time as a reference to a time-of-a-day dimension
|
1275
1270
|
#
|
1276
1271
|
class TimeReference < Reference
|
1277
|
-
|
1278
1272
|
end
|
1279
1273
|
|
1280
1274
|
##
|
@@ -1343,7 +1337,6 @@ module GoodData
|
|
1343
1337
|
def to_manifest_part(mode)
|
1344
1338
|
nil
|
1345
1339
|
end
|
1346
|
-
|
1347
1340
|
end
|
1348
1341
|
|
1349
1342
|
##
|
@@ -1351,8 +1344,10 @@ module GoodData
|
|
1351
1344
|
#
|
1352
1345
|
class Folder < MdObject
|
1353
1346
|
def initialize(title)
|
1347
|
+
# TODO: should a super be here?
|
1348
|
+
# how to deal with name vs title?
|
1354
1349
|
@title = title
|
1355
|
-
@name = title
|
1350
|
+
@name = GoodData::Helpers.sanitize_string(title)
|
1356
1351
|
end
|
1357
1352
|
|
1358
1353
|
def to_maql_create
|
@@ -1366,11 +1361,11 @@ module GoodData
|
|
1366
1361
|
#
|
1367
1362
|
class AttributeFolder < Folder
|
1368
1363
|
def type;
|
1369
|
-
|
1364
|
+
'ATTRIBUTE'
|
1370
1365
|
end
|
1371
1366
|
|
1372
1367
|
def type_prefix;
|
1373
|
-
|
1368
|
+
'dim'
|
1374
1369
|
end
|
1375
1370
|
end
|
1376
1371
|
|
@@ -1379,21 +1374,20 @@ module GoodData
|
|
1379
1374
|
#
|
1380
1375
|
class FactFolder < Folder
|
1381
1376
|
def type;
|
1382
|
-
|
1377
|
+
'FACT'
|
1383
1378
|
end
|
1384
1379
|
|
1385
1380
|
def type_prefix;
|
1386
|
-
|
1381
|
+
'ffld'
|
1387
1382
|
end
|
1388
1383
|
end
|
1389
1384
|
|
1390
1385
|
class DateDimension < MdObject
|
1391
|
-
|
1392
1386
|
def initialize(spec={})
|
1393
1387
|
super()
|
1394
1388
|
@name = spec[:name]
|
1395
1389
|
@title = spec[:title] || @name
|
1396
|
-
@urn = spec[:urn] ||
|
1390
|
+
@urn = spec[:urn] || 'URN:GOODDATA:DATE'
|
1397
1391
|
end
|
1398
1392
|
|
1399
1393
|
def to_maql_create
|
@@ -1401,12 +1395,10 @@ module GoodData
|
|
1401
1395
|
# title = "title"
|
1402
1396
|
# name = "name"
|
1403
1397
|
|
1404
|
-
maql =
|
1398
|
+
maql = ''
|
1405
1399
|
maql += "INCLUDE TEMPLATE \"#{@urn}\" MODIFY (IDENTIFIER \"#{@name}\", TITLE \"#{@title}\");"
|
1406
1400
|
maql
|
1407
1401
|
end
|
1408
|
-
|
1409
1402
|
end
|
1410
|
-
|
1411
1403
|
end
|
1412
1404
|
end
|