gd_bam 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +67 -24
- data/bin/bam +24 -5
- data/lib/bam/version.rb +1 -1
- data/lib/graphs/process_forecast.grf +20 -0
- data/lib/graphs/process_opp_records.grf +84 -0
- data/lib/runtime.rb +23 -10
- data/templates/reformat_template.grf.erb +46 -0
- metadata +7 -10
data/README.md
CHANGED
@@ -35,6 +35,7 @@ You need a working project with API access. You should also have username pass a
|
|
35
35
|
### Warnings
|
36
36
|
The project is currently cloned out of an existing project. That means that you need to have access to it. If you do not (the project PID is i49w4c73c2mh75iiehte3fv3fbos8h2k) ask svarovsky@gooddata.com. Eventually this will be covered by a template so you will not need to do anything special. The template creation is tracked here https://jira.intgdc.com/browse/GD-34641 .
|
37
37
|
|
38
|
+
###Let's get to it
|
38
39
|
We will spin a goodsales project and load it with data. Prerequisite for this is a functioning Salesforce poject that you can grab at force.com.
|
39
40
|
|
40
41
|
`bam scaffold project test --blueprint goodsales`
|
@@ -58,7 +59,7 @@ We will talk in detail why we split etl for project into downloaders and rest of
|
|
58
59
|
|
59
60
|
You can watch the progress in our CloudConnect console.
|
60
61
|
|
61
|
-
Now generate the etl.
|
62
|
+
Now generate the etl.
|
62
63
|
|
63
64
|
`bam generate`
|
64
65
|
|
@@ -68,8 +69,19 @@ This works the same as with downloaders but its default target is clover_project
|
|
68
69
|
|
69
70
|
After it is finished log in to gooddata go into your project and celebrate. You just did project using BAM.
|
70
71
|
|
72
|
+
##Painful metadata management
|
73
|
+
Key pain that I had with CloudConnect is that I hated the management of metadata. Every project I saw was just pile of metadata definition that has to be constantly changed and tweaked. This is caused by couple of chioces that creators of underlying Clover engine made in the beginning and probably will not be changed easily. While I am trying to make it better I am still bound by these choices and sometimes the wiring stick out - sorry for that.
|
74
|
+
|
75
|
+
###Incremental metadata
|
76
|
+
Bam is working with something that is called Incremental metadata. Metadata is not defined in each step you just say what you want to change. Picture is probably better than thousand words.
|
77
|
+
|
78
|
+
You have a conceptual picture of a simple transformation. You get a Tap that downloads FirstName and LastName somewhere. Obviously you would like to join them together to form a name. Exactly this happens in the second box the transformer. You would like to sink the only field and that is name. So on the next edge what you say is "I am adding Name and removing FirstName and LastName". So far so good. What is elegant about this approach is that how it copes with change. Imagine that the tap gets not only FirstName and LastName but also Age. Now what you need to change? If you would do it the old way You would have to change metadata on both edges, tap transformer and sink. With incremental metadata you need to change tap and sink nothing else. Since I claim that dealing with metadata was the biggest pain this is a lot of work (and errors) that you just saved.
|
79
|
+
|
80
|
+
###Types or not?
|
81
|
+
Clover engine is built on Java and it shows. It is statically typed and CTL Clover transformation language resembles Java a lot. While it helps speed and many people claim it prevents errors it also causes more work and helps metadata explosion. Sometimes you need to translate an field into another field becuase you need to do something specific or the component needs it. It is not problem per se but it is important to see the tradeoffs and push the functionality into the components that should work for you and not against you. It is also important to do certain tasks at certain phases. If you do this you found out that certain parts are easier to automate or you can easily reuse work that you did somewhere else.
|
82
|
+
|
71
83
|
##Taps
|
72
|
-
Taps are sources of data.
|
84
|
+
Taps are sources of data. Right now you can use just salesforce tap.
|
73
85
|
|
74
86
|
###Common properties
|
75
87
|
Every tap has the source and an id. Source tells it where to go to grab data. DB, CSV, SalesForce. Depending on the type the definition might be different. Id is a field that holds a name with which you can reference particular tap. Obviously it needs to be unique.
|
@@ -155,10 +167,10 @@ Fail early. There is nothing more frustrating than when the ETL fails during exw
|
|
155
167
|
####Mandatory fields
|
156
168
|
Sometimes it is necessary to move fields around in SF. In such case the tap will. If you know this upfront you can tell BAM that this field is not mandatory and it will silently go along filling the missing field with ''
|
157
169
|
|
158
|
-
|
170
|
+
##Flows
|
171
|
+
Flow is an abstraction
|
159
172
|
|
160
|
-
|
161
|
-
This tap will download users from sf (you have to provide credentials in params.json). It then runs graph called "process user" (this is part of the distribution). This graph concatenates first name and last name together.
|
173
|
+
This flow will download users from sf (you have to provide credentials in params.json). It then runs graph called "process user" (this is part of the distribution). This graph concatenates first name and last name together. It then feeds data to the sink.
|
162
174
|
|
163
175
|
GoodData::CloverGenerator::DSL::flow("user") do |f|
|
164
176
|
tap(:id => "user")
|
@@ -175,29 +187,59 @@ This tap will download users from sf (you have to provide credentials in params.
|
|
175
187
|
|
176
188
|
Now you have to provide it the definition of tap which you can do like this.
|
177
189
|
|
190
|
+
###When I call external graph? How does it work?
|
191
|
+
In the flow you can call external graph by using
|
178
192
|
|
193
|
+
graph('my_graph')
|
194
|
+
|
195
|
+
It goes to 2 places (this will change) and tries to find the graph. First place is your `local_graphs` directory in your project the second place is central reporsitory that is currently inside bam library and this part will probably change.
|
179
196
|
|
180
|
-
|
197
|
+
##Sinks
|
181
198
|
|
182
|
-
|
183
|
-
"type" : "dataset"
|
184
|
-
,"id" : "user"
|
185
|
-
,"gd_name" : "user"
|
186
|
-
,"fields" : [
|
187
|
-
{
|
188
|
-
"name" : "Id"
|
189
|
-
},
|
190
|
-
{
|
191
|
-
"name" : "Name"
|
192
|
-
}
|
193
|
-
]
|
194
|
-
}
|
199
|
+
Sink is a definition of where data goes to. Currently there is only one sink type and that is gooddata dataset.
|
195
200
|
|
196
|
-
|
201
|
+
###GoodData
|
202
|
+
|
203
|
+
{
|
204
|
+
"type" : "dataset",
|
205
|
+
"id" : "account",
|
206
|
+
"gd_name" : "account",
|
207
|
+
"fields": [
|
208
|
+
{
|
209
|
+
"name" : "id",
|
210
|
+
"type" : "connection_point",
|
211
|
+
"meta" : "Id"
|
212
|
+
},
|
213
|
+
{
|
214
|
+
"type" : "label",
|
215
|
+
"for" : "id",
|
216
|
+
"name" : "name",
|
217
|
+
"meta" : "Name"
|
218
|
+
},
|
219
|
+
{
|
220
|
+
"type" : "label",
|
221
|
+
"for" : "id",
|
222
|
+
"name" : "url",
|
223
|
+
"meta" : "Url"
|
224
|
+
},
|
225
|
+
{
|
226
|
+
"name" : "market",
|
227
|
+
"type" : "attribute",
|
228
|
+
"meta" : "Market__c"
|
229
|
+
}
|
230
|
+
]
|
231
|
+
}
|
232
|
+
|
233
|
+
Gooddat sink is currently just mimicking the CL tool definition + some shortcuts on top of that. If you are familiar with CL tool you should be right at home if I tell you that the only additional thing you have to provide is telling BAM which metadata field is pulled in to a given field.
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
<!--For this example to work you need to provide SF and gd credentials. Provide them in params.json. You would need to provide also a project with appropriate project but this is out of scope of this "example" (I am working on tools that would make it easier).
|
197
238
|
|
198
239
|
Now run `bam generate` and there will be a folder with the clover project generated. Open it in CC find main.grf and run it. After crunching for a while you should see data in the project.
|
240
|
+
-->
|
199
241
|
|
200
|
-
|
242
|
+
##Runtime commands
|
201
243
|
Part of the distribution is the bam executable which lets you do several neat things on the commandline
|
202
244
|
|
203
245
|
Run `bam` to get the list of commands
|
@@ -207,7 +249,7 @@ Run `bam help command` to get help about the command
|
|
207
249
|
deploys the directory to the server. You can provide the param of the process as a parameter
|
208
250
|
|
209
251
|
### generate
|
210
|
-
Generates the ETL. The default target directory is clover_project (currently cannot be changed). You can provide --only parameter to specify the name of the flow to be processed if you do not need to generate all flows. Currently you can specify only
|
252
|
+
Generates the ETL. The default target directory is clover_project (currently cannot be changed). You can provide --only parameter to specify the name of the flow to be processed if you do not need to generate all flows. Currently you can specify only one flow
|
211
253
|
|
212
254
|
### generate_downloaders
|
213
255
|
If you have incremental downloaders in your project it good to deploy them as a separate process. This generates only the downloaders and is meant for exacltly this purpose. If you are interested about why it is a good idea. Take a look here (TBD). The target directory is downloaders_project (currently cannot be changed).
|
@@ -216,10 +258,11 @@ If you have incremental downloaders in your project it good to deploy them as a
|
|
216
258
|
Investigates what is changed and performs the changes in the target project. Uses CL tool behind the scenes. Needs more work
|
217
259
|
|
218
260
|
### model_sync
|
219
|
-
Syncs the model with the definition in sinks.
|
261
|
+
Syncs the model with the definition in sinks. Sometimes the new field can actually be a typo or something like that. Possible to uncover with validate_datasets
|
220
262
|
|
221
263
|
### run
|
222
|
-
|
264
|
+
Runs the project and
|
265
|
+
`bam run clover-project --email me@gooddata.com`
|
223
266
|
|
224
267
|
### scaffold
|
225
268
|
Takes an argument and creates a scaffold for you. It can scaffold project, flow, sink and tap.
|
data/bin/bam
CHANGED
@@ -31,6 +31,15 @@ command :generate do |c|
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
desc 'Jacks into SF.'
|
35
|
+
# arg_name 'Describe arguments to new here'
|
36
|
+
command :sf_jack_in do |c|
|
37
|
+
|
38
|
+
c.action do |global_options,options,args|
|
39
|
+
GoodData::CloverGenerator.sf_jack_in
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
34
43
|
desc 'Generates clover project for downloaders.'
|
35
44
|
# arg_name 'Describe arguments to new here'
|
36
45
|
command :generate_downloaders do |c|
|
@@ -106,7 +115,8 @@ command :project do |c|
|
|
106
115
|
when nil
|
107
116
|
fail "Empty project not supported now"
|
108
117
|
end
|
109
|
-
|
118
|
+
|
119
|
+
GoodData::CloverGenerator.connect_to_gd()
|
110
120
|
with_users = options[:with_users]
|
111
121
|
|
112
122
|
export = {
|
@@ -184,7 +194,7 @@ command :scaffold do |c|
|
|
184
194
|
|
185
195
|
c.action do |global_options,options,args|
|
186
196
|
command = args.first
|
187
|
-
fail "You did not provide what I should scaffold. I can generate project, tap, flow, sink nothing else" unless ["project", "tap", "flow", "sink"].include?(command)
|
197
|
+
fail "You did not provide what I should scaffold. I can generate project, tap, flow, sink, graph_template nothing else" unless ["project", "tap", "flow", "sink", "graph_template"].include?(command)
|
188
198
|
case command
|
189
199
|
when "project"
|
190
200
|
directory = args[1]
|
@@ -209,6 +219,12 @@ command :scaffold do |c|
|
|
209
219
|
name = args[1]
|
210
220
|
fail "Name of the sink has to be provided as an argument. See help" if name.nil?
|
211
221
|
GoodData::CloverGenerator.setup_sink(name)
|
222
|
+
when "graph_template"
|
223
|
+
name = args[1]
|
224
|
+
target = args[2]
|
225
|
+
fail "Name of the template has to be provided as an argument. See help" if name.nil?
|
226
|
+
fail "Name of the target has to be provided as an argument. See help" if target.nil?
|
227
|
+
GoodData::CloverGenerator.generate_graph_template(name, target)
|
212
228
|
end
|
213
229
|
end
|
214
230
|
end
|
@@ -262,13 +278,16 @@ command :run do |c|
|
|
262
278
|
|
263
279
|
verbose = global_options[:v]
|
264
280
|
|
265
|
-
|
281
|
+
logger = Logger.new(STDOUT) if global_options[:l]
|
282
|
+
|
283
|
+
|
284
|
+
GoodData::CloverGenerator.connect_to_gd(:logger => logger)
|
266
285
|
options = global_options.merge({:name => "temporary"})
|
267
286
|
GoodData::CloverGenerator.deploy(dir, options) do |deploy_response|
|
268
287
|
puts HighLine::color("Executing", HighLine::BOLD) if verbose
|
269
288
|
GoodData::CloverGenerator.create_email_channel(options) do |channel_response|
|
270
|
-
GoodData::CloverGenerator.subscribe_on_finish(:success, channel_response["channelConfiguration"]["meta"]["uri"], deploy_response["
|
271
|
-
GoodData::CloverGenerator.execute_process(deploy_response["
|
289
|
+
GoodData::CloverGenerator.subscribe_on_finish(:success, channel_response["channelConfiguration"]["meta"]["uri"], deploy_response["process"]["links"]["self"].split('/').last)
|
290
|
+
GoodData::CloverGenerator.execute_process(deploy_response["process"]["links"]["executions"], dir)
|
272
291
|
end
|
273
292
|
end
|
274
293
|
end
|
data/lib/bam/version.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Graph author="fluke" created="Tue Feb 05 15:38:24 PST 2013" guiVersion="3.3.2" id="1360179808937" licenseType="Commercial" modified="Thu May 02 12:37:08 PDT 2013" modifiedBy="gdc-defectivedisplay" name="process_name" revision="1.14" showComponentDetails="true">
|
3
|
+
<Global>
|
4
|
+
<Metadata fileURL="${PROJECT}/metadata/${FLOW}/${NAME}/1_in.xml" id="Metadata0"/>
|
5
|
+
<Metadata fileURL="${PROJECT}/metadata/${FLOW}/${NAME}/1_out.xml" id="Metadata1"/>
|
6
|
+
<MetadataGroup id="ComponentGroup0" name="metadata"/>
|
7
|
+
<Property fileURL="params.txt" id="GraphParameter0"/>
|
8
|
+
<Property fileURL="workspace.prm" id="GraphParameter2"/>
|
9
|
+
<Dictionary/>
|
10
|
+
</Global>
|
11
|
+
<Phase number="0">
|
12
|
+
<Node enabled="enabled" fileURL="data/1_in.csv" guiHeight="77" guiName="CSV Reader" guiWidth="128" guiX="37" guiY="169" id="DATA_READER0" quoteCharacter=""" quotedStrings="true" skipRows="1" type="DATA_READER"/>
|
13
|
+
<Node enabled="enabled" fileURL="data/out.csv" guiHeight="77" guiName="CSV Writer" guiWidth="128" guiX="609" guiY="169" id="DATA_WRITER0" outputFieldNames="true" quoteCharacter=""" quotedStrings="true" type="DATA_WRITER"/>
|
14
|
+
<Node dedupKey="ForecastCategoryName(a)" enabled="enabled" guiHeight="65" guiName="Dedup" guiWidth="128" guiX="368" guiY="160" id="DEDUP0" type="DEDUP"/>
|
15
|
+
<Node enabled="enabled" guiHeight="89" guiName="ExtSort" guiWidth="128" guiX="199" guiY="175" id="EXT_SORT0" sortKey="ForecastCategoryName(a)" type="EXT_SORT"/>
|
16
|
+
<Edge fromNode="DATA_READER0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge0" inPort="Port 0 (in)" metadata="Metadata0" outPort="Port 0 (output)" toNode="EXT_SORT0:0"/>
|
17
|
+
<Edge fromNode="DEDUP0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge1" inPort="Port 0 (in)" metadata="Metadata1" outPort="Port 0 (unique)" toNode="DATA_WRITER0:0"/>
|
18
|
+
<Edge fromNode="EXT_SORT0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge2" inPort="Port 0 (in)" metadata="Metadata0" outPort="Port 0 (out)" toNode="DEDUP0:0"/>
|
19
|
+
</Phase>
|
20
|
+
</Graph>
|
@@ -0,0 +1,84 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Graph author="gdc-defectivedisplay" created="Sat Mar 16 12:07:29 PDT 2013" guiVersion="3.3.2" id="1363571865202" licenseCode="CLP1DGOODD71636137BY" licenseType="Commercial" modified="Thu Apr 11 13:57:16 PDT 2013" modifiedBy="Jagjit" name="xxxx" revision="1.55" showComponentDetails="true">
|
3
|
+
<Global>
|
4
|
+
<Metadata fileURL="${PROJECT}/metadata/${FLOW}/${NAME}/1_in.xml" id="Metadata0"/>
|
5
|
+
<Metadata fileURL="${PROJECT}/metadata/${FLOW}/${NAME}/1_out.xml" id="Metadata1"/>
|
6
|
+
<Metadata id="Metadata2">
|
7
|
+
<Record fieldDelimiter="|" name="recordName1" recordDelimiter="\n" type="delimited">
|
8
|
+
<Field name="field1" type="string"/>
|
9
|
+
</Record>
|
10
|
+
</Metadata>
|
11
|
+
<Property fileURL="params.txt" id="GraphParameter0"/>
|
12
|
+
<Property fileURL="workspace.prm" id="GraphParameter2"/>
|
13
|
+
<Dictionary/>
|
14
|
+
</Global>
|
15
|
+
<Phase number="0">
|
16
|
+
<Node enabled="enabled" fileURL="data/1_in.csv" guiHeight="77" guiName="CSV Reader" guiWidth="128" guiX="83" guiY="90" id="DATA_READER0" quoteCharacter=""" quotedStrings="true" skipRows="1" type="DATA_READER"/>
|
17
|
+
<Node enabled="enabled" guiHeight="65" guiName="Reformat" guiWidth="128" guiX="282" guiY="80" id="REFORMAT1" type="REFORMAT">
|
18
|
+
<attr name="transform"><![CDATA[//#CTL2
|
19
|
+
|
20
|
+
// Transforms input record into output record.
|
21
|
+
function integer transform() {
|
22
|
+
$out.0.* = $in.0.*;
|
23
|
+
|
24
|
+
if ($in.0.Probability == null) {
|
25
|
+
$out.0.Probability = "0";
|
26
|
+
} else {
|
27
|
+
$out.0.Probability = toString(str2decimal($in.0.Probability)/100D);
|
28
|
+
}
|
29
|
+
|
30
|
+
if ($in.0.Amount == null) {
|
31
|
+
$out.0.Amount = "0";
|
32
|
+
} else {
|
33
|
+
$out.0.Amount = toString(round(str2decimal($in.0.Amount)*100D)/100D);
|
34
|
+
}
|
35
|
+
|
36
|
+
if ($in.0.CloseDate != null && $in.0.CloseDate != "" && left($in.0.CloseDate, 4) < "1950") {
|
37
|
+
$out.0.CloseDate = '1950-01-01';
|
38
|
+
}
|
39
|
+
|
40
|
+
if ($in.0.CloseDate != null && $in.0.CloseDate != "" && left($in.0.CloseDate, 4) > "2049") {
|
41
|
+
$out.0.CloseDate = '2049-01-01';
|
42
|
+
}
|
43
|
+
|
44
|
+
if ($in.0.CreatedDate != null && $in.0.CreatedDate != "" && left($in.0.CreatedDate, 4) < "1950") {
|
45
|
+
$out.0.CreatedDate = '1950-01-01';
|
46
|
+
}
|
47
|
+
|
48
|
+
if ($in.0.CreatedDate != null && $in.0.CreatedDate != "" && left($in.0.CreatedDate, 4) > "2049") {
|
49
|
+
$out.0.CreatedDate = '2049-01-01';
|
50
|
+
}
|
51
|
+
|
52
|
+
if ($in.0.CreatedDate != null && $in.0.CreatedDate != "" ) {
|
53
|
+
$out.0.CreatedDate = left($in.0.CreatedDate, 10);
|
54
|
+
}
|
55
|
+
|
56
|
+
return ALL;
|
57
|
+
}
|
58
|
+
|
59
|
+
// Called during component initialization.
|
60
|
+
// function boolean init() {}
|
61
|
+
|
62
|
+
// Called during each graph run before the transform is executed. May be used to allocate and initialize resources
|
63
|
+
// required by the transform. All resources allocated within this method should be released
|
64
|
+
// by the postExecute() method.
|
65
|
+
// function void preExecute() {}
|
66
|
+
|
67
|
+
// Called only if transform() throws an exception.
|
68
|
+
// function integer transformOnError(string errorMessage, string stackTrace) {}
|
69
|
+
|
70
|
+
// Called during each graph run after the entire transform was executed. Should be used to free any resources
|
71
|
+
// allocated within the preExecute() method.
|
72
|
+
// function void postExecute() {}
|
73
|
+
|
74
|
+
// Called to return a user-defined error message when an error occurs.
|
75
|
+
// function string getMessage() {}
|
76
|
+
]]></attr>
|
77
|
+
</Node>
|
78
|
+
<Edge fromNode="DATA_READER0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge1" inPort="Port 0 (in)" metadata="Metadata0" outPort="Port 0 (output)" toNode="REFORMAT1:0"/>
|
79
|
+
<Edge fromNode="REFORMAT1:0" guiBendpoints="" guiRouter="Manhattan" id="Edge0" inPort="Port 0 (in)" metadata="Metadata1" outPort="Port 0 (out)" toNode="DATA_WRITER0:0"/>
|
80
|
+
</Phase>
|
81
|
+
<Phase number="1">
|
82
|
+
<Node enabled="enabled" fileURL="data/out.csv" guiHeight="77" guiName="CSV Writer" guiWidth="128" guiX="517" guiY="74" id="DATA_WRITER0" outputFieldNames="true" quotedStrings="true" type="DATA_WRITER"/>
|
83
|
+
</Phase>
|
84
|
+
</Graph>
|
data/lib/runtime.rb
CHANGED
@@ -55,12 +55,15 @@ module GoodData
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def self.setup_clover_project(base)
|
58
|
+
def self.setup_clover_project(base, options={})
|
59
|
+
|
60
|
+
name = options[:name] || PARAMS[:project_name]
|
61
|
+
|
59
62
|
[PROJECT_GRAPHS_ROOT, PROJECT_METADATA_ROOT, PROJECT_DATA_ROOT].each do |dir|
|
60
63
|
FileUtils::mkdir_p base + dir
|
61
64
|
end
|
62
65
|
FileUtils::touch(base + 'params.txt')
|
63
|
-
render_template("project.erb", PARAMS.merge(:project_name =>
|
66
|
+
render_template("project.erb", PARAMS.merge(:project_name => name), :to_file => base + '.project')
|
64
67
|
render_template("workspace.prm.erb", PARAMS, :to_file => base + 'workspace.prm')
|
65
68
|
end
|
66
69
|
|
@@ -138,6 +141,11 @@ module GoodData
|
|
138
141
|
pp report
|
139
142
|
end
|
140
143
|
|
144
|
+
def self.sf_jack_in
|
145
|
+
client = get_sf_client(PARAMS)
|
146
|
+
binding.pry
|
147
|
+
end
|
148
|
+
|
141
149
|
def self.generate_docs
|
142
150
|
project = build_project
|
143
151
|
sources = project.get_sources
|
@@ -209,7 +217,7 @@ module GoodData
|
|
209
217
|
end
|
210
218
|
|
211
219
|
def self.generate_downloaders(options={})
|
212
|
-
setup_clover_project(CLOVER_DOWNLOADERS_ROOT)
|
220
|
+
setup_clover_project(CLOVER_DOWNLOADERS_ROOT, :name => "downloaders-#{PARAMS[:project_name]}")
|
213
221
|
project = build_project
|
214
222
|
sources = project.get_sources
|
215
223
|
sf_sources = sources.find_all {|tap| tap[:source] == "salesforce" && tap[:incremental] == true}
|
@@ -226,16 +234,16 @@ module GoodData
|
|
226
234
|
|
227
235
|
def self.execute_process(link, dir)
|
228
236
|
result = GoodData.post(link, {
|
229
|
-
:
|
237
|
+
:execution => {
|
230
238
|
:graph => "./#{dir}/graphs/main.grf",
|
231
239
|
:params => {}
|
232
240
|
}
|
233
241
|
})
|
234
|
-
GoodData.poll(result, "
|
242
|
+
GoodData.poll(result, "executionTask")
|
235
243
|
end
|
236
244
|
|
237
|
-
def self.connect_to_gd
|
238
|
-
GoodData.logger =
|
245
|
+
def self.connect_to_gd(options={})
|
246
|
+
GoodData.logger = options[:logger]
|
239
247
|
GoodData.connect(PARAMS[:gd_login], PARAMS[:gd_pass])
|
240
248
|
GoodData.project = PARAMS[:project_pid] if !PARAMS[:project_pid].nil? && !PARAMS[:project_pid].empty?
|
241
249
|
end
|
@@ -333,7 +341,7 @@ module GoodData
|
|
333
341
|
process_id = options[:process]
|
334
342
|
|
335
343
|
data = {
|
336
|
-
:
|
344
|
+
:process => {
|
337
345
|
:name => deploy_name || "#{PARAMS[:project_name]}",
|
338
346
|
:path => "/uploads/#{File.basename(temp.path)}"
|
339
347
|
}
|
@@ -355,7 +363,7 @@ module GoodData
|
|
355
363
|
res = deploy_graph(dir, options)
|
356
364
|
block.call(res)
|
357
365
|
ensure
|
358
|
-
self_link = res["
|
366
|
+
self_link = res["process"]["links"]["self"]
|
359
367
|
GoodData.delete(self_link)
|
360
368
|
end
|
361
369
|
else
|
@@ -363,11 +371,16 @@ module GoodData
|
|
363
371
|
end
|
364
372
|
end
|
365
373
|
|
374
|
+
def self.generate_graph_template(name, target)
|
375
|
+
template_name = "#{name}_template.grf.erb"
|
376
|
+
render_template(template_name, PARAMS, :to_file => USER_DEFINED_GRAPHS_ROOT + target)
|
377
|
+
end
|
378
|
+
|
366
379
|
|
367
380
|
def self.run(options)
|
368
381
|
|
369
382
|
only_flow = options[:only]
|
370
|
-
setup_clover_project(CLOVER_PROJECT_ROOT)
|
383
|
+
setup_clover_project(CLOVER_PROJECT_ROOT, :name => "etl-#{PARAMS[:project_name]}")
|
371
384
|
p = build_project
|
372
385
|
sources = p.get_sources
|
373
386
|
datasets = p.get_datasets
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Graph author="fluke" created="Tue Feb 05 15:38:24 PST 2013" guiVersion="3.3.1" id="1360179808937" licenseType="Commercial" modified="Fri Feb 22 13:36:18 PST 2013" modifiedBy="fluke" name="process_name" revision="1.12" showComponentDetails="true">
|
3
|
+
<Global>
|
4
|
+
<Metadata fileURL="${PROJECT}/metadata/${FLOW}/${NAME}/1_in.xml" id="Metadata0"/>
|
5
|
+
<Metadata fileURL="${PROJECT}/metadata/${FLOW}/${NAME}/1_out.xml" id="Metadata1"/>
|
6
|
+
<MetadataGroup id="ComponentGroup0" name="metadata"/>
|
7
|
+
<Property fileURL="params.txt" id="GraphParameter0"/>
|
8
|
+
<Property fileURL="workspace.prm" id="GraphParameter1"/>
|
9
|
+
<Dictionary/>
|
10
|
+
</Global>
|
11
|
+
<Phase number="0">
|
12
|
+
<Node enabled="enabled" fileURL="data/1_in.csv" guiHeight="77" guiName="CSV Reader" guiWidth="128" guiX="124" guiY="169" id="DATA_READER0" quoteCharacter=""" quotedStrings="true" skipRows="1" type="DATA_READER"/>
|
13
|
+
<Node enabled="enabled" fileURL="data/out.csv" guiHeight="77" guiName="CSV Writer" guiWidth="128" guiX="609" guiY="169" id="DATA_WRITER0" outputFieldNames="true" quoteCharacter=""" quotedStrings="true" type="DATA_WRITER"/>
|
14
|
+
<Node enabled="enabled" guiHeight="65" guiName="Reformat" guiWidth="128" guiX="365" guiY="175" id="REFORMAT0" type="REFORMAT">
|
15
|
+
<attr name="transform"><![CDATA[//#CTL2
|
16
|
+
|
17
|
+
// Transforms input record into output record.
|
18
|
+
function integer transform() {
|
19
|
+
$out.0.* = $in.0.*;
|
20
|
+
|
21
|
+
return ALL;
|
22
|
+
}
|
23
|
+
|
24
|
+
// Called during component initialization.
|
25
|
+
// function boolean init() {}
|
26
|
+
|
27
|
+
// Called during each graph run before the transform is executed. May be used to allocate and initialize resources
|
28
|
+
// required by the transform. All resources allocated within this method should be released
|
29
|
+
// by the postExecute() method.
|
30
|
+
// function void preExecute() {}
|
31
|
+
|
32
|
+
// Called only if transform() throws an exception.
|
33
|
+
// function integer transformOnError(string errorMessage, string stackTrace) {}
|
34
|
+
|
35
|
+
// Called during each graph run after the entire transform was executed. Should be used to free any resources
|
36
|
+
// allocated within the preExecute() method.
|
37
|
+
// function void postExecute() {}
|
38
|
+
|
39
|
+
// Called to return a user-defined error message when an error occurs.
|
40
|
+
// function string getMessage() {}
|
41
|
+
]]></attr>
|
42
|
+
</Node>
|
43
|
+
<Edge fromNode="DATA_READER0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge0" inPort="Port 0 (in)" metadata="Metadata0" outPort="Port 0 (output)" toNode="REFORMAT0:0"/>
|
44
|
+
<Edge fromNode="REFORMAT0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge1" inPort="Port 0 (in)" metadata="Metadata1" outPort="Port 0 (out)" toNode="DATA_WRITER0:0"/>
|
45
|
+
</Phase>
|
46
|
+
</Graph>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gd_bam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -258,7 +258,7 @@ dependencies:
|
|
258
258
|
requirements:
|
259
259
|
- - '='
|
260
260
|
- !ruby/object:Gem::Version
|
261
|
-
version: 0.5.
|
261
|
+
version: 0.5.11
|
262
262
|
type: :runtime
|
263
263
|
prerelease: false
|
264
264
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -266,7 +266,7 @@ dependencies:
|
|
266
266
|
requirements:
|
267
267
|
- - '='
|
268
268
|
- !ruby/object:Gem::Version
|
269
|
-
version: 0.5.
|
269
|
+
version: 0.5.11
|
270
270
|
- !ruby/object:Gem::Dependency
|
271
271
|
name: highline
|
272
272
|
requirement: !ruby/object:Gem::Requirement
|
@@ -350,6 +350,8 @@ files:
|
|
350
350
|
- lib/graphs/process_activity.grf
|
351
351
|
- lib/graphs/process_activity_dim.grf
|
352
352
|
- lib/graphs/process_activity_owner.grf
|
353
|
+
- lib/graphs/process_forecast.grf
|
354
|
+
- lib/graphs/process_opp_records.grf
|
353
355
|
- lib/graphs/process_opportunity.grf
|
354
356
|
- lib/graphs/process_opportunity_line_item.grf
|
355
357
|
- lib/graphs/process_opportunity_snapshot.grf
|
@@ -368,6 +370,7 @@ files:
|
|
368
370
|
- templates/flow.rb.erb
|
369
371
|
- templates/params.json.erb
|
370
372
|
- templates/project.erb
|
373
|
+
- templates/reformat_template.grf.erb
|
371
374
|
- templates/source.json.erb
|
372
375
|
- templates/tap.json.erb
|
373
376
|
- templates/update_dataset.script.erb
|
@@ -392,18 +395,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
392
395
|
- - ! '>='
|
393
396
|
- !ruby/object:Gem::Version
|
394
397
|
version: '0'
|
395
|
-
segments:
|
396
|
-
- 0
|
397
|
-
hash: 3491844236925090228
|
398
398
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
399
399
|
none: false
|
400
400
|
requirements:
|
401
401
|
- - ! '>='
|
402
402
|
- !ruby/object:Gem::Version
|
403
403
|
version: '0'
|
404
|
-
segments:
|
405
|
-
- 0
|
406
|
-
hash: 3491844236925090228
|
407
404
|
requirements: []
|
408
405
|
rubyforge_project:
|
409
406
|
rubygems_version: 1.8.25
|