gooddata 0.5.16 → 0.6.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|