gooddata 0.5.16 → 0.6.0.pre2
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/Gemfile +2 -0
- data/bin/gooddata +291 -8
- data/gooddata.gemspec +14 -5
- data/lib/gooddata/client.rb +34 -5
- data/lib/gooddata/commands/api.rb +27 -30
- data/lib/gooddata/commands/process.rb +137 -0
- data/lib/gooddata/commands/profile.rb +5 -5
- data/lib/gooddata/commands/projects.rb +107 -40
- data/lib/gooddata/commands/runners.rb +37 -0
- data/lib/gooddata/commands/scaffold.rb +30 -0
- data/lib/gooddata/connection.rb +31 -19
- data/lib/gooddata/extract.rb +1 -1
- data/lib/gooddata/goodzilla/goodzilla.rb +40 -0
- data/lib/gooddata/model.rb +418 -138
- data/lib/gooddata/models/attribute.rb +24 -0
- data/lib/gooddata/models/dashboard.rb +60 -0
- data/lib/gooddata/models/data_result.rb +4 -6
- data/lib/gooddata/models/data_set.rb +20 -0
- data/lib/gooddata/models/display_form.rb +7 -0
- data/lib/gooddata/models/fact.rb +17 -0
- data/lib/gooddata/models/metadata.rb +69 -17
- data/lib/gooddata/models/metric.rb +90 -0
- data/lib/gooddata/models/process.rb +112 -0
- data/lib/gooddata/models/profile.rb +1 -1
- data/lib/gooddata/models/project.rb +85 -29
- data/lib/gooddata/models/report.rb +45 -0
- data/lib/gooddata/models/report_definition.rb +139 -0
- data/lib/gooddata/version.rb +1 -1
- data/lib/templates/bricks/brick.rb.erb +7 -0
- data/lib/templates/bricks/main.rb.erb +4 -0
- data/spec/goodzilla_spec.rb +57 -0
- data/spec/model_dsl_spec.rb +22 -0
- data/test/test_commands.rb +1 -1
- data/test/test_model.rb +6 -6
- metadata +137 -16
- data/bin/igd.rb +0 -33
- data/lib/gooddata/command.rb +0 -75
- data/lib/gooddata/commands/help.rb +0 -104
- data/lib/gooddata/commands/version.rb +0 -7
- data/test/helper.rb +0 -13
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'zip
|
1
|
+
require 'zip'
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
4
|
module GoodData
|
@@ -18,7 +18,7 @@ module GoodData
|
|
18
18
|
def all
|
19
19
|
json = GoodData.get GoodData.profile.projects
|
20
20
|
json['projects'].map do |project|
|
21
|
-
Project.new project
|
21
|
+
Project.new project
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -29,6 +29,7 @@ module GoodData
|
|
29
29
|
# - <id>
|
30
30
|
#
|
31
31
|
def [](id)
|
32
|
+
return id if id.respond_to?(:is_project?) && id.is_project?
|
32
33
|
if id.to_s !~ /^(\/gdc\/(projects|md)\/)?[a-zA-Z\d]+$/
|
33
34
|
raise ArgumentError.new("wrong type of argument. Should be either project ID or path")
|
34
35
|
end
|
@@ -36,7 +37,7 @@ module GoodData
|
|
36
37
|
id = id.match(/[a-zA-Z\d]+$/)[0] if id =~ /\//
|
37
38
|
|
38
39
|
response = GoodData.get PROJECT_PATH % id
|
39
|
-
Project.new response
|
40
|
+
Project.new response
|
40
41
|
end
|
41
42
|
|
42
43
|
# Create a project from a given attributes
|
@@ -45,26 +46,32 @@ module GoodData
|
|
45
46
|
# - :summary
|
46
47
|
# - :template (default /projects/blank)
|
47
48
|
#
|
48
|
-
def create(attributes)
|
49
|
+
def create(attributes, &block)
|
49
50
|
GoodData.logger.info "Creating project #{attributes[:title]}"
|
50
51
|
|
51
52
|
auth_token = attributes.delete(:auth_token) || GoodData.connection.auth_token
|
52
53
|
|
53
|
-
json = {
|
54
|
-
|
55
|
-
'
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
54
|
+
json = {:project =>
|
55
|
+
{
|
56
|
+
'meta' => {
|
57
|
+
'title' => attributes[:title],
|
58
|
+
'summary' => attributes[:summary] || "No summary"
|
59
|
+
},
|
60
|
+
'content' => {
|
61
|
+
'guidedNavigation' => 1,
|
62
|
+
'authorizationToken' => auth_token,
|
63
|
+
"driver" => "Pg"
|
64
|
+
}
|
62
65
|
}
|
63
66
|
}
|
64
|
-
|
65
67
|
json['meta']['projectTemplate'] = attributes[:template] if attributes[:template] && !attributes[:template].empty?
|
66
68
|
project = Project.new json
|
67
69
|
project.save
|
70
|
+
if block
|
71
|
+
GoodData::with_project(project) do |p|
|
72
|
+
block.call(p)
|
73
|
+
end
|
74
|
+
end
|
68
75
|
project
|
69
76
|
end
|
70
77
|
end
|
@@ -74,20 +81,29 @@ module GoodData
|
|
74
81
|
end
|
75
82
|
|
76
83
|
def save
|
77
|
-
response = GoodData.post PROJECTS_PATH,
|
84
|
+
response = GoodData.post PROJECTS_PATH, raw_data
|
78
85
|
if uri == nil
|
79
86
|
response = GoodData.get response['uri']
|
80
|
-
@json = response
|
87
|
+
@json = response
|
81
88
|
end
|
82
89
|
end
|
83
90
|
|
84
91
|
def delete
|
85
92
|
raise "Project '#{title}' with id #{uri} is already deleted" if state == :deleted
|
86
|
-
GoodData.delete
|
93
|
+
GoodData.delete data['links']['self']
|
87
94
|
end
|
88
95
|
|
89
96
|
def uri
|
90
|
-
|
97
|
+
data['links']['self'] if data && data['links'] && data['links']['self']
|
98
|
+
end
|
99
|
+
|
100
|
+
def browser_uri(options={})
|
101
|
+
ui = options[:ui]
|
102
|
+
if ui
|
103
|
+
GoodData.connection.url + "#s=" + uri
|
104
|
+
else
|
105
|
+
GoodData.connection.url + uri
|
106
|
+
end
|
91
107
|
end
|
92
108
|
|
93
109
|
def obj_id
|
@@ -95,15 +111,15 @@ module GoodData
|
|
95
111
|
end
|
96
112
|
|
97
113
|
def title
|
98
|
-
|
114
|
+
data['meta']['title'] if data['meta']
|
99
115
|
end
|
100
116
|
|
101
117
|
def state
|
102
|
-
|
118
|
+
data['content']['state'].downcase.to_sym if data['content'] && data['content']['state']
|
103
119
|
end
|
104
120
|
|
105
121
|
def md
|
106
|
-
@md ||= Links.new GoodData.get(
|
122
|
+
@md ||= Links.new GoodData.get(data['links']['metadata'])
|
107
123
|
end
|
108
124
|
|
109
125
|
# Creates a data set within the project
|
@@ -112,11 +128,38 @@ module GoodData
|
|
112
128
|
# p.add_dataset 'Test', [ { 'name' => 'a1', 'type' => 'ATTRIBUTE' ... } ... ]
|
113
129
|
# p.add_dataset 'title' => 'Test', 'columns' => [ { 'name' => 'a1', 'type' => 'ATTRIBUTE' ... } ... ]
|
114
130
|
#
|
115
|
-
def add_dataset(
|
116
|
-
schema =
|
117
|
-
|
118
|
-
|
119
|
-
|
131
|
+
def add_dataset(schema_def, columns = nil, &block)
|
132
|
+
schema = if block
|
133
|
+
builder = block.call(Model::SchemaBuilder.new(schema_def))
|
134
|
+
builder.to_schema
|
135
|
+
else
|
136
|
+
sch = { :title => schema_def, :columns => columns } if columns
|
137
|
+
sch = Model::Schema.new schema_def if schema_def.is_a? Hash
|
138
|
+
sch = schema_def if schema_def.is_a?(Model::Schema)
|
139
|
+
raise ArgumentError.new("Required either schema object or title plus columns array") unless schema_def.is_a? Model::Schema
|
140
|
+
sch
|
141
|
+
end
|
142
|
+
Model.add_schema(schema, self)
|
143
|
+
end
|
144
|
+
|
145
|
+
def add_metric(options={})
|
146
|
+
expression = options[:expression] || fail("Metric has to have its expression defined")
|
147
|
+
m1 = GoodData::Metric.create(options)
|
148
|
+
m1.save
|
149
|
+
end
|
150
|
+
|
151
|
+
def add_report(options={})
|
152
|
+
rep = GoodData::Report.create(options)
|
153
|
+
rep.save
|
154
|
+
end
|
155
|
+
|
156
|
+
def add_dashboard(options={})
|
157
|
+
dash = GoodData::Dashboard.create(options)
|
158
|
+
dash.save
|
159
|
+
end
|
160
|
+
|
161
|
+
def add_user(email_address, domain)
|
162
|
+
|
120
163
|
end
|
121
164
|
|
122
165
|
def upload(file, schema, mode = "FULL")
|
@@ -124,7 +167,7 @@ module GoodData
|
|
124
167
|
end
|
125
168
|
|
126
169
|
def slis
|
127
|
-
link = "#{
|
170
|
+
link = "#{data['links']['metadata']}#{SLIS_PATH}"
|
128
171
|
Metadata.new GoodData.get(link)
|
129
172
|
end
|
130
173
|
|
@@ -132,12 +175,25 @@ module GoodData
|
|
132
175
|
datasets_uri = "#{md['data']}/sets"
|
133
176
|
response = GoodData.get datasets_uri
|
134
177
|
response['dataSetsInfo']['sets'].map do |ds|
|
135
|
-
DataSet
|
178
|
+
DataSet[ds["meta"]["uri"]]
|
136
179
|
end
|
137
180
|
end
|
138
181
|
|
139
|
-
def
|
182
|
+
def raw_data
|
140
183
|
@json
|
141
184
|
end
|
185
|
+
|
186
|
+
def data
|
187
|
+
raw_data["project"]
|
188
|
+
end
|
189
|
+
|
190
|
+
def to_json
|
191
|
+
raw_data.to_json
|
192
|
+
end
|
193
|
+
|
194
|
+
def is_project?
|
195
|
+
true
|
196
|
+
end
|
197
|
+
|
142
198
|
end
|
143
199
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module GoodData
|
2
2
|
class Report < GoodData::MdObject
|
3
3
|
|
4
|
+
root_key :report
|
5
|
+
|
4
6
|
class << self
|
5
7
|
def [](id)
|
6
8
|
if id == :all
|
@@ -9,8 +11,51 @@ module GoodData
|
|
9
11
|
super
|
10
12
|
end
|
11
13
|
end
|
14
|
+
|
15
|
+
def create(options={})
|
16
|
+
title = options[:title]
|
17
|
+
summary = options[:summary] || ""
|
18
|
+
rd = options[:rd] || ReportDefinition.create(:top => options[:top], :left => options[:left])
|
19
|
+
rd.save
|
20
|
+
|
21
|
+
report = Report.new({
|
22
|
+
"report" => {
|
23
|
+
"content" => {
|
24
|
+
"domains" => [],
|
25
|
+
"definitions" => [rd.uri]
|
26
|
+
},
|
27
|
+
"meta" => {
|
28
|
+
"tags" => "",
|
29
|
+
"deprecated" => "0",
|
30
|
+
"summary" => summary,
|
31
|
+
"title" => title
|
32
|
+
}
|
33
|
+
}
|
34
|
+
})
|
35
|
+
end
|
12
36
|
end
|
13
37
|
|
38
|
+
# ----
|
39
|
+
|
40
|
+
def title=(a_title)
|
41
|
+
@json["report"]["meta"]["title"] = a_title
|
42
|
+
end
|
43
|
+
|
44
|
+
def summary=(a_summary)
|
45
|
+
@json[:report][:meta][:summary] = a_summary
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_definition(a_definition)
|
49
|
+
@json[:report][:content][:definitions] << a_definition
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_json(options={})
|
53
|
+
JSON.pretty_generate(@json, options)
|
54
|
+
end
|
55
|
+
# ----
|
56
|
+
|
57
|
+
|
58
|
+
|
14
59
|
def results
|
15
60
|
content["results"]
|
16
61
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module GoodData
|
2
|
+
class ReportDefinition < GoodData::MdObject
|
3
|
+
|
4
|
+
root_key :reportDefinition
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def [](id)
|
8
|
+
if id == :all
|
9
|
+
GoodData.get(GoodData.project.md['query'] + '/reportdefinition/')['query']['entries']
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_metrics_part(left, top)
|
16
|
+
stuff = Array(left) + Array(top)
|
17
|
+
stuff.find_all {|item| item.respond_to?(:is_metric?)}.map do |metric|
|
18
|
+
create_metric_part(metric)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_metric_part(metric)
|
23
|
+
{
|
24
|
+
"alias" => metric.title,
|
25
|
+
"uri" => metric.uri
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_attribute_part(attrib)
|
30
|
+
{
|
31
|
+
"attribute" => {
|
32
|
+
"alias" => "",
|
33
|
+
"totals" => [],
|
34
|
+
"uri" => attrib.uri
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_part(stuff)
|
40
|
+
stuff = Array(stuff)
|
41
|
+
parts = stuff.reduce([]) do |memo, item|
|
42
|
+
if item.respond_to?(:is_metric?)
|
43
|
+
memo
|
44
|
+
else
|
45
|
+
memo << create_attribute_part(item)
|
46
|
+
end
|
47
|
+
memo
|
48
|
+
end
|
49
|
+
if stuff.any? {|item| item.respond_to?(:is_metric?)}
|
50
|
+
parts << "metricGroup"
|
51
|
+
end
|
52
|
+
parts
|
53
|
+
end
|
54
|
+
|
55
|
+
def find(stuff)
|
56
|
+
stuff.map do |item|
|
57
|
+
if item.respond_to?(:is_attribute?)
|
58
|
+
item.display_forms.first
|
59
|
+
elsif item.is_a?(String)
|
60
|
+
GoodData::Attribute.find_first_by_title(item).display_forms.first
|
61
|
+
elsif item.is_a?(Hash) && item[:type].to_s == "metric"
|
62
|
+
GoodData::Metric.find_first_by_title(item[:title])
|
63
|
+
elsif item.is_a?(Hash) && item[:type].to_s == "attribute"
|
64
|
+
GoodData::Attribute.find_first_by_title(item[:title]).display_forms.first
|
65
|
+
else
|
66
|
+
item
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def execute(options={})
|
72
|
+
left = Array(options[:left])
|
73
|
+
top = Array(options[:top])
|
74
|
+
|
75
|
+
metrics = (left + top).find_all {|item| item.respond_to?(:is_metric?)}
|
76
|
+
|
77
|
+
unsaved_metrics = metrics.reject {|i| i.saved?}
|
78
|
+
unsaved_metrics.each {|m| m.title = "Untitled metric" unless m.title}
|
79
|
+
|
80
|
+
begin
|
81
|
+
unsaved_metrics.each {|m| m.save}
|
82
|
+
rd = GoodData::ReportDefinition.create(options)
|
83
|
+
rd.save
|
84
|
+
rd.execute
|
85
|
+
ensure
|
86
|
+
rd.delete if rd.saved? rescue nil
|
87
|
+
unsaved_metrics.each {|m| m.delete if m.saved?}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def create(options={})
|
92
|
+
left = Array(options[:left])
|
93
|
+
top = Array(options[:top])
|
94
|
+
|
95
|
+
left = ReportDefinition.find(left)
|
96
|
+
top = ReportDefinition.find(top)
|
97
|
+
|
98
|
+
ReportDefinition.new({
|
99
|
+
"reportDefinition" => {
|
100
|
+
"content" => {
|
101
|
+
"grid" => {
|
102
|
+
"sort" => {
|
103
|
+
"columns" => [],
|
104
|
+
"rows" => []
|
105
|
+
},
|
106
|
+
"columnWidths" => [],
|
107
|
+
"columns" => ReportDefinition.create_part(top),
|
108
|
+
"metrics" => ReportDefinition.create_metrics_part(left, top),
|
109
|
+
"rows" => ReportDefinition.create_part(left),
|
110
|
+
},
|
111
|
+
"format" => "grid",
|
112
|
+
"filters" => []
|
113
|
+
},
|
114
|
+
"meta" => {
|
115
|
+
"tags" => "",
|
116
|
+
"summary" => "",
|
117
|
+
"title" => "Untitled report definition"
|
118
|
+
}
|
119
|
+
}
|
120
|
+
})
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def metrics
|
125
|
+
content["grid"]["metrics"].map {|i| GoodData::Metric[i["uri"]]}
|
126
|
+
end
|
127
|
+
|
128
|
+
def execute
|
129
|
+
result = GoodData.post '/gdc/xtab2/executor3', {"report_req" => {"reportDefinition" => uri}}
|
130
|
+
data_result_uri = result["execResult"]["dataResult"]
|
131
|
+
result = GoodData.get data_result_uri
|
132
|
+
while result["taskState"] && result["taskState"]["status"] == "WAIT" do
|
133
|
+
sleep 10
|
134
|
+
result = GoodData.get data_result_uri
|
135
|
+
end
|
136
|
+
ReportDataResult.new(GoodData.get data_result_uri)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
data/lib/gooddata/version.rb
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'gooddata/goodzilla/goodzilla'
|
2
|
+
|
3
|
+
describe GoodData::SmallGoodZilla do
|
4
|
+
|
5
|
+
MAQL_EXAMPLE = 'SELECT SUM(#"Amount") WHERE @"Date"=#"X" AND ?"Snapshot EOP"=1'
|
6
|
+
FACTS = {
|
7
|
+
"Amount" => "a",
|
8
|
+
"X" => "x"
|
9
|
+
}
|
10
|
+
ATTRIBUTES = {
|
11
|
+
"Date" => "d"
|
12
|
+
}
|
13
|
+
METRICS = {
|
14
|
+
"Snapshot EOP" => "snap"
|
15
|
+
}
|
16
|
+
DICT = {
|
17
|
+
:facts => FACTS,
|
18
|
+
:attributes => ATTRIBUTES,
|
19
|
+
:metrics => METRICS,
|
20
|
+
}
|
21
|
+
|
22
|
+
it "should parse metrics out of the string" do
|
23
|
+
x = GoodData::SmallGoodZilla.get_facts(MAQL_EXAMPLE)
|
24
|
+
x.should == ["Amount", "X"]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should parse attributes out of the string" do
|
28
|
+
x = GoodData::SmallGoodZilla.get_attributes(MAQL_EXAMPLE)
|
29
|
+
x.should == ["Date"]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should parse metrics out of the string" do
|
33
|
+
x = GoodData::SmallGoodZilla.get_metrics(MAQL_EXAMPLE)
|
34
|
+
x.should == ["Snapshot EOP"]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should interpolate the values" do
|
38
|
+
|
39
|
+
interpolated = GoodData::SmallGoodZilla.interpolate({
|
40
|
+
:facts => ["Amount", "X"],
|
41
|
+
:attributes => ["Date"],
|
42
|
+
:metrics => ["Snapshot EOP"]
|
43
|
+
}, DICT)
|
44
|
+
|
45
|
+
interpolated.should == {
|
46
|
+
:facts => [["Amount", "a"], ["X", "x"]],
|
47
|
+
:attributes => [["Date", "d"]],
|
48
|
+
:metrics => [["Snapshot EOP", "snap"]]
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should return interpolated metric" do
|
53
|
+
interpolated = GoodData::SmallGoodZilla.interpolate_metric(MAQL_EXAMPLE, DICT)
|
54
|
+
interpolated.should == "SELECT SUM([a]) WHERE [d]=[x] AND [snap]=1"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|