mountain-goat 0.1.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +119 -48
- data/generators/mg/mg_generator.rb +17 -9
- data/generators/mg/templates/create_mountain_goat_tables.rb +61 -25
- data/generators/mg/templates/mg.rb +1 -0
- data/generators/mg/templates/mountain-goat.yml +22 -3
- data/generators/mg/templates/mountain_goat_reports.rake +17 -0
- data/generators/mg/templates/update_mountain_goat_tables.rb +104 -0
- data/lib/mountain-goat.rb +21 -12
- data/lib/mountain-goat/analytics.rb +134 -0
- data/lib/mountain-goat/controllers/{mountain_goat/mountain_goat_converts_controller.rb → mg/converts_controller.rb} +12 -11
- data/lib/mountain-goat/controllers/{mountain_goat/mountain_goat_metric_variants_controller.rb → mg/metric_variants_controller.rb} +6 -5
- data/lib/mountain-goat/controllers/{mountain_goat/mountain_goat_metrics_controller.rb → mg/metrics_controller.rb} +8 -6
- data/lib/mountain-goat/controllers/mg/mg.rb +47 -0
- data/lib/mountain-goat/controllers/{mountain_goat → mg}/mountain_goat_controller.rb +5 -42
- data/lib/mountain-goat/controllers/mg/playground_controller.rb +8 -0
- data/lib/mountain-goat/controllers/{mountain_goat/mountain_goat_rallies_controller.rb → mg/rallies_controller.rb} +4 -26
- data/lib/mountain-goat/controllers/mg/report_items_controller.rb +82 -0
- data/lib/mountain-goat/controllers/mg/reports_controller.rb +90 -0
- data/lib/mountain-goat/m_g.rb +64 -0
- data/lib/mountain-goat/metric_tracking.rb +192 -74
- data/lib/mountain-goat/models/mg/ci_meta.rb +10 -0
- data/lib/mountain-goat/models/mg/convert.rb +147 -0
- data/lib/mountain-goat/models/mg/convert_meta_type.rb +20 -0
- data/lib/mountain-goat/models/mg/cs_meta.rb +10 -0
- data/lib/mountain-goat/models/{metric.rb → mg/metric.rb} +3 -5
- data/lib/mountain-goat/models/mg/metric_variant.rb +25 -0
- data/lib/mountain-goat/models/mg/mountain_goat.rb +3 -0
- data/lib/mountain-goat/models/{rally.rb → mg/rally.rb} +4 -3
- data/lib/mountain-goat/models/mg/report.rb +11 -0
- data/lib/mountain-goat/models/mg/report_item.rb +24 -0
- data/lib/mountain-goat/models/mg/report_mailer.rb +18 -0
- data/lib/mountain-goat/public/g-bar-min.js +7 -0
- data/lib/mountain-goat/public/g-dot-min.js +7 -0
- data/lib/mountain-goat/public/g-line-min.js +7 -0
- data/lib/mountain-goat/public/g-pie-min.js +1 -0
- data/lib/mountain-goat/public/g-raphael-min.js +7 -0
- data/lib/mountain-goat/public/jqModel.css +41 -0
- data/lib/mountain-goat/public/jqModel.js +69 -0
- data/lib/mountain-goat/public/jquery.raphael.js +208 -0
- data/lib/mountain-goat/public/mg.css +135 -26
- data/lib/mountain-goat/public/mg.js +53 -1
- data/lib/mountain-goat/public/raphael-min.js +7 -0
- data/lib/mountain-goat/public/utils.js +520 -0
- data/lib/mountain-goat/switch_variant.rb +4 -4
- data/lib/mountain-goat/version.rb +1 -1
- data/lib/mountain-goat/views/mountain_goat/layouts/_pdf.html.erb +15 -0
- data/lib/mountain-goat/views/mountain_goat/layouts/mountain_goat.html.erb +17 -5
- data/lib/mountain-goat/views/mountain_goat/layouts/xhr.html.erb +2 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_converts → mg/converts}/.tmp_show.html.erb.4433~ +0 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_converts → mg/converts}/_convert_form.html.erb +0 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_converts → mg/converts}/_convert_meta_type_form.html.erb +0 -0
- data/lib/mountain-goat/views/mountain_goat/mg/converts/edit.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/mg/converts/index.html.erb +25 -0
- data/lib/mountain-goat/views/mountain_goat/mg/converts/new.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_converts → mg/converts}/show.html.erb +3 -28
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_metric_variants → mg/metric_variants}/_metric_variant_form.html.erb +0 -4
- data/lib/mountain-goat/views/mountain_goat/mg/metric_variants/edit.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_metric_variants → mg/metric_variants}/index.html.erb +3 -3
- data/lib/mountain-goat/views/mountain_goat/mg/metric_variants/new.html.erb +15 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_metric_variants → mg/metric_variants}/show.html.erb +4 -5
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_metrics → mg/metrics}/.tmp_show.html.erb.21270~ +0 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_metrics → mg/metrics}/_metric_form.html.erb +0 -2
- data/lib/mountain-goat/views/mountain_goat/mg/metrics/edit.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/mg/metrics/index.html.erb +14 -0
- data/lib/mountain-goat/views/mountain_goat/mg/metrics/new.html.erb +14 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_metrics → mg/metrics}/show.html.erb +7 -8
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat → mg/mountain_goat}/login.html.erb +0 -0
- data/lib/mountain-goat/views/mountain_goat/mg/playground/test.html.erb +14 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_rallies → mg/rallies}/.tmp__rally.html.erb.40484~ +0 -0
- data/lib/mountain-goat/views/mountain_goat/mg/rallies/_rallies.html.erb +5 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_rallies → mg/rallies}/_rallies_form.html.erb +0 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_rallies → mg/rallies}/_rally.html.erb +0 -0
- data/lib/mountain-goat/views/mountain_goat/mg/rallies/edit.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_rallies → mg/rallies}/index.html.erb +1 -1
- data/lib/mountain-goat/views/mountain_goat/mg/rallies/new.html.erb +13 -0
- data/lib/mountain-goat/views/mountain_goat/{mountain_goat_rallies → mg/rallies}/show.html.erb +1 -1
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/_chart.html.erb +18 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/_report_item_form.html.erb +10 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/_report_item_pivot_form.html.erb +14 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/_show.html.erb +29 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/_svg_chart.html.erb +4 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/edit.html.erb +19 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_items/new.html.erb +17 -0
- data/lib/mountain-goat/views/mountain_goat/mg/report_mailer/report.html.erb +27 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/_report.html.erb +22 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/_report_form.html.erb +21 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/_report_report_items.html.erb +5 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/edit.html.erb +36 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/index.html.erb +26 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/new.html.erb +17 -0
- data/lib/mountain-goat/views/mountain_goat/mg/reports/show.html.erb +21 -0
- data/test/fixtures/{ci_metas.yml → mg_ci_metas.yml} +0 -0
- data/test/fixtures/{convert_meta_types.yml → mg_convert_meta_types.yml} +0 -0
- data/test/fixtures/{converts.yml → mg_converts.yml} +5 -0
- data/test/fixtures/{cs_metas.yml → mg_cs_metas.yml} +0 -0
- data/test/fixtures/mg_deliveries.yml +9 -0
- data/test/fixtures/{metric_variants.yml → mg_metric_variants.yml} +0 -0
- data/test/fixtures/{metrics.yml → mg_metrics.yml} +2 -3
- data/test/fixtures/{rallies.yml → mg_rallies.yml} +6 -2
- data/test/fixtures/mg_report_items.yml +13 -0
- data/test/fixtures/mg_reports.yml +15 -0
- data/test/mg_convert_test.rb +32 -0
- data/test/mg_converts_controller_test.rb +47 -0
- data/test/mg_metric_variants_controller_test.rb +46 -0
- data/test/mg_metrics_controller_test.rb +52 -0
- data/test/mg_mountain_goat_controller_test.rb +45 -0
- data/test/mg_mountain_goat_test.rb +392 -0
- data/test/mg_playground_controller_test.rb +11 -0
- data/test/mg_rallies_controller_test.rb +36 -0
- data/test/mg_report_item_test.rb +7 -0
- data/test/mg_report_items_controller_test.rb +31 -0
- data/test/mg_report_test.rb +18 -0
- data/test/mg_reports_controller_test.rb +50 -0
- data/test/test_helper.rb +203 -0
- metadata +108 -55
- data/lib/mountain-goat/models/ci_meta.rb +0 -9
- data/lib/mountain-goat/models/convert.rb +0 -70
- data/lib/mountain-goat/models/convert_meta_type.rb +0 -19
- data/lib/mountain-goat/models/cs_meta.rb +0 -9
- data/lib/mountain-goat/models/metric_variant.rb +0 -22
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_converts/edit.html.erb +0 -13
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_converts/index.html.erb +0 -48
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_converts/new.html.erb +0 -13
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_metric_variants/edit.html.erb +0 -13
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_metric_variants/new.html.erb +0 -15
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_metrics/edit.html.erb +0 -13
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_metrics/index.html.erb +0 -14
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_metrics/new.html.erb +0 -14
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_rallies/_rallies.html.erb +0 -5
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_rallies/edit.html.erb +0 -13
- data/lib/mountain-goat/views/mountain_goat/mountain_goat_rallies/new.html.erb +0 -13
- data/test/ocelot_converts_controller_test.rb +0 -45
- data/test/ocelot_metric_variants_controller_test.rb +0 -45
- data/test/ocelot_metrics_controller_test.rb +0 -45
- data/test/ocelot_rallies_controller_test.rb +0 -8
data/README.md
CHANGED
@@ -1,25 +1,33 @@
|
|
1
1
|
# Mountain-Goat
|
2
2
|
|
3
|
-
Embed a
|
3
|
+
Embed a professional analytics and reporting platform into your application in minutes. Gain important business insights through bandit (automated a/b) testing, view the results and analytics in real time, and get delivered daily or weekly usage reports.
|
4
|
+
|
5
|
+
Note: For updating from version < 1.0.0, please read the **Upgrade** section below.
|
4
6
|
|
5
7
|
Add simple hooks in your code to display a/b metrics
|
6
8
|
|
7
|
-
<%=
|
9
|
+
<%= bd(:homescreen_text, "Welcome here") %>
|
8
10
|
|
9
|
-
This creates a database entry
|
11
|
+
This creates a database entry for your a/b test "homescreen_text". Visit "http://yourdomain.com/mg" and you can add / adjust options (variants) for this text. When a user converts on a goal, you run the following code.
|
10
12
|
|
13
|
+
#e.g. users_controller.rb
|
11
14
|
def create
|
12
15
|
record_conversion(:user_signup)
|
13
16
|
...
|
14
17
|
end
|
15
18
|
|
16
|
-
This will track a conversion not only for the goal, but for the variant of "homescreen_text"
|
19
|
+
This will track a conversion not only for the goal, but for the variant of "homescreen_text" that the user was served when your user came to the home-page.
|
20
|
+
|
21
|
+
Bandit testing (see [Wikipedia - Multi-armed Bandit](http://en.wikipedia.org/wiki/Multi-armed_bandit) automatically converges on the variant that achieves the highest success through exploration each variant. You essentially get a Bayesian solution with no hassle!
|
17
22
|
|
18
|
-
The best part? The
|
23
|
+
The best part? The Mountain-Goat Administrative console is located on your server and you can view and analyze your data in real time (or through setting up usage report emails).
|
19
24
|
|
20
25
|
- See which metric variants are working and not working ("Cowabunga!" did 120% better than "Enter here", but 10% worse than "Do it rockapella!")
|
21
|
-
-
|
22
|
-
-
|
26
|
+
- Get daily / weekly / whenever emails delivered to your inbox, full of the data you crave (e.g. Signs up by day / comments by referring domain )
|
27
|
+
- Similar to Bayesian learning, Multi-armed bandit solutions will automatically deliver the highest performing variants
|
28
|
+
- This is done while still achieving minimal (logarithmically decreasing) regret (showing of poorer performing variants)
|
29
|
+
- A/B testing? How about A/B/C/D/E testing? Add as many variants as you like.
|
30
|
+
- Visually analyze the your metric variants; change them on the fly, adding new ones or kicking out poor performers.
|
23
31
|
- Watch goal conversions in real-time with live-action console (grab the popcorn and watch how your users "sign up" and "view items" and ...)
|
24
32
|
- You can do more than change text, "switch variants" let you enter arbitrary ruby code, change the control of your site ("Do my users have to sign-in before commenting? Let's test!")
|
25
33
|
- Track goals with meta data (record_conversion(:user_signup, :referrer => request.env['HTTP_REFERER']))
|
@@ -30,13 +38,23 @@ The best part? The mountain-goat admin console is located on your server and yo
|
|
30
38
|
|
31
39
|
For more information, read my blog post on how mountain goat quickly accomplishes a/b testing: http://blog.drawn.to/how-we-do-ab-testing
|
32
40
|
|
41
|
+
## Upgrade from < 1.0.0
|
42
|
+
|
43
|
+
If you are upgrading from Mountain Goat < 1.0.0, please run the following command (please overwrite mountain-goat.yml when prompted):
|
44
|
+
|
45
|
+
./script/generate mg --update=1.0.0
|
46
|
+
|
47
|
+
rake db:migrate
|
48
|
+
|
49
|
+
This will install new migrations necessary for version 1.0.0.
|
50
|
+
|
33
51
|
## Install
|
34
52
|
|
35
53
|
### Mountain Goat gem
|
36
54
|
|
37
55
|
gem install mountain-goat
|
38
56
|
|
39
|
-
### Mountain Goat
|
57
|
+
### Mountain Goat configuration
|
40
58
|
|
41
59
|
Next run generator to create config file and necessary database migration (optionally pass in --password=my_mg_password)
|
42
60
|
|
@@ -45,7 +63,8 @@ Next run generator to create config file and necessary database migration (optio
|
|
45
63
|
This will generate
|
46
64
|
|
47
65
|
/config/mountain-goat.yml (for storing a password to access mountain-goat)
|
48
|
-
/db/migrate/xxx_create_mountain_goat_tables.rb (necessary
|
66
|
+
/db/migrate/xxx_create_mountain_goat_tables.rb (necessary database migrations to store mg data)
|
67
|
+
/lib/tasks/mountain_goat_reports.rake (add tasks for report delivery)
|
49
68
|
|
50
69
|
Modify /config/mountain-goat.yml to setup a password for each environment of your product
|
51
70
|
|
@@ -66,27 +85,51 @@ Mountain Goat hinges around three core concepts:
|
|
66
85
|
|
67
86
|
After you set up your database with some mountain-goat tables, the code will handle populating these tables for you. In your code, you can start A/B testing immediately.
|
68
87
|
|
69
|
-
<h2><%=
|
88
|
+
<h2><%= bd(:banner_on_store_front, "Now arsenic and gluten-free") %></h2>
|
70
89
|
|
71
|
-
The
|
90
|
+
The bandit (bd) function takes two parameters:
|
72
91
|
|
73
|
-
|
92
|
+
bd(metric_name, default)
|
74
93
|
|
75
|
-
This will automatically create a metric and
|
76
|
-
|
77
|
-
From here, you can go into the mountain goat admin center and add new metric variants to fit your need. It's all built
|
94
|
+
This will automatically create a metric and populate a metric variant with the default value. Easy, eh?
|
95
|
+
|
96
|
+
From here, you can go into the mountain goat admin center and add new metric variants to fit your need. It's all built into *your* application, in house.
|
78
97
|
|
79
98
|
http://{your_rails_app}/mg (e.g. if you're at railsrocks.com, then visit http://railsrocks.com/mg)
|
80
99
|
|
81
100
|
The other important code you'll need to implement is to tell the system when a goal is achieved.
|
82
101
|
|
83
102
|
def purchase #in coffees_controller.rb
|
84
|
-
|
103
|
+
rw(:user_purchases_coffee, 10) #10 "points"
|
85
104
|
...
|
86
105
|
end
|
87
106
|
|
88
|
-
This will go in and record a conversion (a "rally") for a user purchasing coffee. Further, it will track a hit for any metric-variants served to that user
|
89
|
-
|
107
|
+
This will go in and record a conversion (a "rally") for a user purchasing coffee. Further, it will track a hit for any metric-variants served to that user. For example "Chuck Norris works here" might get reward points. You will see which metrics lead to a conversion; this is the core of A/B and bandit testing.
|
108
|
+
|
109
|
+
## Bandit Testing
|
110
|
+
|
111
|
+
Why points? Bandit testing is about maximizing a return. The original idea is if I have a slot machine with 30 handles each with different payouts, how do I maximize my return? You pull the arm that has given you the best pay out so far, but occasionally pull other arms to make sure there's not something better. Mountain-Goat's Bandit system will do the same for you.
|
112
|
+
|
113
|
+
The most common solution to the multi-armed bandit solution is epsilon-greedy (documented [here](http://www.cs.nyu.edu/~mohri/pub/bandit.pdf)).
|
114
|
+
|
115
|
+
Given epsilon in [0, 1] #commonly 0.05 - 0.15
|
116
|
+
|
117
|
+
If any variant has never been pulled
|
118
|
+
Pull that variant
|
119
|
+
Otherwise
|
120
|
+
|
121
|
+
If random(0..1) < epsilon
|
122
|
+
Pull random arm
|
123
|
+
Else
|
124
|
+
Pull winning arm #highest "reward"
|
125
|
+
End
|
126
|
+
|
127
|
+
This way, we most commonly explore the best arm (e.g. your best slogan) while occasionally trying other arms.
|
128
|
+
|
129
|
+
A small variant on this is called epsilon-greedy-decreasing, which reduces epsilon each pull by 1/total_pulls. Thus, after 100 pulls, epsilon = epsilon_0 / 100. This is shown to have an optimal minimal expectation of regret.
|
130
|
+
|
131
|
+
You can configure these options in `mountain-goat.yml`.
|
132
|
+
|
90
133
|
## Mountain Goat admin suite
|
91
134
|
|
92
135
|
Navigate to /mg in your rails application (on your actual server instance) to reach the mountain-goat admin center. Here, you can analyze / adjust your A/B tests.
|
@@ -110,7 +153,21 @@ Go to "Metrics" and visit a specific metric. You'll see which metric variants a
|
|
110
153
|
###Rallies
|
111
154
|
|
112
155
|
Rallies shows you what's going on, in real time. You will see conversions (goals) being hit by your clients in real time. Grab a bag of pop-corn and watch users struggle (or glide) across your site. Add meta data to get further information. This page automatically updates as new rallies come in.
|
113
|
-
|
156
|
+
|
157
|
+
###Reports
|
158
|
+
|
159
|
+
In the Mountain Goat Administrative suite, you can add reports. Reports will be delivered as emails with an attached pdf showing statistics about your product. You'll need the following installed to use Mountain Goat Reports.
|
160
|
+
|
161
|
+
gem install pdfkit
|
162
|
+
gem install svg-graph
|
163
|
+
|
164
|
+
You'll need [wkhtmltopdf](http://code.google.com/p/wkhtmltopdf/) installed. Please configure the location of the executable in `mountain-goat.yml`.
|
165
|
+
|
166
|
+
Finally, simply set up a cron task on your system when you would like your reports delivered. E.g.
|
167
|
+
|
168
|
+
crontab -l | { cat; echo "45 6 * * * cd /path/to/project && /usr/bin/rake RAILS_ENV=production mg:deliver[daily] >> /path/to/project/log/cron.log 2>&1"; } | crontab -
|
169
|
+
crontab -l | { cat; echo "10 6 1 * * cd /path/to/project && /usr/bin/rake RAILS_ENV=production mg:deliver[monthly] >> $app_root/current/log/cron.log 2>&1"; } | crontab -
|
170
|
+
|
114
171
|
## Advanced Features
|
115
172
|
|
116
173
|
### Meta data
|
@@ -135,19 +192,13 @@ Instead of just serving text, you can also serve flow control in Mountain Goat,
|
|
135
192
|
discount = 0.90
|
136
193
|
end
|
137
194
|
|
138
|
-
variant.whomp_whomp
|
195
|
+
variant.whomp_whomp do
|
139
196
|
discount = 0.0
|
140
197
|
end
|
141
198
|
end
|
142
199
|
|
143
|
-
Mountain goat will automatically break those down into three cases (:ten_percent, :big_winner, :whomp_whomp) and serve them out at random to the user
|
144
|
-
|
145
|
-
### Priorities
|
200
|
+
Mountain goat will automatically break those down into three cases (:ten_percent, :big_winner, :whomp_whomp) and serve them out at random to the user.
|
146
201
|
|
147
|
-
You may want to test certain items with a lower serve rate (bold new slogans). You can assign priorities to any metric variant. The change of a given metric variant being shown is
|
148
|
-
|
149
|
-
my priority / sum(all priorities for this metric)
|
150
|
-
|
151
202
|
### Meta Options
|
152
203
|
|
153
204
|
There is certain meta data that you may wish to collect for a number of different conversions. For example, you may want to track ip-address so you can later pivot this column to find new / returning users. To do this, add an initializer that calls MountainGoat.add_meta_option().
|
@@ -167,30 +218,50 @@ Then, when we track the conversion, you'll get meta-data for the user's ip-addre
|
|
167
218
|
As mountain goat is a suite that is added into your project dynamically, the following routes and tables are added during setup:
|
168
219
|
|
169
220
|
- Tables
|
170
|
-
*
|
171
|
-
*
|
172
|
-
*
|
173
|
-
*
|
174
|
-
*
|
175
|
-
*
|
176
|
-
*
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
*
|
182
|
-
*
|
183
|
-
*
|
184
|
-
*
|
185
|
-
*
|
186
|
-
*
|
187
|
-
*
|
188
|
-
*
|
221
|
+
* mg_ci_metas (indexes: ci_metas_cmt_data_index, ci_metas_cmt_index, ci_metas_rally_index)
|
222
|
+
* mg_convert_meta_types
|
223
|
+
* mg_converts
|
224
|
+
* mg_cs_metas (indexes: cs_metas_cmt_data_index, cs_metas_cmt_index, cs_metas_rally_index)
|
225
|
+
* mg_metric_variants
|
226
|
+
* mg_metrics
|
227
|
+
* mg_rallies
|
228
|
+
* mg_report_items
|
229
|
+
* mg_reports
|
230
|
+
|
231
|
+
- Routes (all namespaced mg)
|
232
|
+
* mg.mg '/mg', :controller => :converts, :action => :index, :path_prefix => ""
|
233
|
+
* mg.login '/login', :controller => :mountain_goat, :action => :login
|
234
|
+
* mg.login_create '/login/create', :controller => :mountain_goat, :action => :login_create
|
235
|
+
* mg.resources :metric_variants
|
236
|
+
* mg.resources :converts, :has_many => [ :rallies ]
|
237
|
+
* mg.resources :metrics, :has_many => :metric_variants
|
238
|
+
* mg.resources :rallies, :collection => { :new_rallies => :get }
|
239
|
+
* mg.resources :reports, :has_many => :report_items, :member => { :show_svg => :get }
|
240
|
+
* mg.resources :report_items, :member => { :destroy => :get, :update => :post }, :collection => { :get_extra => :get }
|
241
|
+
* mg.resources :playground, :collection => { :test => :get }
|
242
|
+
* mg.new_rallies '/rallies/new', :controller => :rallies, :action => :new_rallies
|
243
|
+
* mg.fresh_metrics '/fresh-metrics', :controller => :metrics, :action => :fresh_metrics
|
244
|
+
* mg.connect '/public/:file', :controller => :mountain_goat, :action => :fetch
|
189
245
|
|
246
|
+
- Models
|
247
|
+
* Mg::CiMeta - Integer-typed meta data for Rallies (e.g. 'Click Count')
|
248
|
+
* Mg::ConvertMetaType - Meta-types for Rallies
|
249
|
+
* Mg::Convert - Goals (e.g. 'Page View', 'User Sign-up')
|
250
|
+
* Mg::CsMeta - String-typed meta data for Rallies (e.g. 'Referring domain')
|
251
|
+
* Mg::MetricVariant - Variant for a/b testing (e.g. 'Come see our store!')
|
252
|
+
* Mg::Metric - Type to vary for a/b testing (e.g. 'Homescreen Text')
|
253
|
+
* Mg::Rally - Instance of a goal conversion (e.g. when a user clicks sign up)
|
254
|
+
* Mg::ReportItem - Item to show in a report (e.g. Sign ups by day)
|
255
|
+
* Mg::Report - Report to deliver (e.g. collection of user report items)
|
256
|
+
|
257
|
+
## Change log
|
258
|
+
1.0.0 - Changed from a/b testing to multi-armed bandit
|
259
|
+
- Added Mountain Goat Reporting
|
260
|
+
- Added extensive test cases for stability
|
261
|
+
|
190
262
|
## TODO
|
191
263
|
- Better documentation (rdocs)
|
192
|
-
- Add namespacing to avoid conflicts
|
193
264
|
|
194
265
|
## Copyright
|
195
266
|
|
196
|
-
Copyright (c) 2011 Geoffrey Hayes,
|
267
|
+
Copyright (c) 2011 Geoffrey Hayes, meloncard.com. Contact me, Geoff, <geoff@meloncard.com> with any questions / ideas / enhancements. See LICENSE for details.
|
@@ -1,9 +1,11 @@
|
|
1
1
|
class MgGenerator < Rails::Generator::Base
|
2
2
|
def add_options!(opt)
|
3
3
|
opt.on('-p', '--password=password', String, "Your password to access Mountain Goat") { |v| options[:password] = v}
|
4
|
+
opt.on('-w', '--wkhtmltopdf=/path/to/dir', String, "Path to installation of wkhtmltopdf (optional)") { |v| options[:wkhtmltopdf] = v}
|
5
|
+
opt.on('-u', '--update=1.0.0', String, "If you have previously installed Mountain Goat, use to generate *update* tables.") { |v| options[:update] = v}
|
4
6
|
puts <<-HELPFUL_INSTRUCTIONS
|
5
7
|
|
6
|
-
Mountain Goat is your home for
|
8
|
+
Mountain Goat is your home for in-house bandit testing.
|
7
9
|
|
8
10
|
We have installed your configuration file to config/mountain-goat.yml
|
9
11
|
Please choose a password for each environment in this file. Make
|
@@ -18,20 +20,26 @@ class MgGenerator < Rails::Generator::Base
|
|
18
20
|
1) Choose an admin-password in config/mountain-goat.yml
|
19
21
|
2) Run `rake db:migrate`
|
20
22
|
3) Start your server and navigate to 'http://mydomain.com/mg'
|
21
|
-
4) Play around, read the docs,
|
23
|
+
4) Play around, read the docs, bandit, enjoy.
|
22
24
|
|
23
25
|
HELPFUL_INSTRUCTIONS
|
24
26
|
end
|
25
27
|
|
26
28
|
def manifest
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
29
|
+
password = options[:password] || ""
|
30
|
+
wkhtmltopdf = options[:wkhtmltopdf] || ""
|
31
|
+
update = options[:update]
|
32
|
+
|
32
33
|
record do |m|
|
33
|
-
|
34
|
-
m.
|
34
|
+
m.template 'mountain_goat_reports.rake', 'lib/tasks/mountain_goat_reports.rake'
|
35
|
+
m.template 'mountain-goat.yml', 'config/mountain-goat.yml', :assigns => { :password => password, :wkhtmltopdf => wkhtmltopdf }
|
36
|
+
|
37
|
+
if !update.blank?
|
38
|
+
m.migration_template 'create_mountain_goat_tables.rb', 'db/migrate', { :migration_file_name => "create_mountain_goat_tables" }
|
39
|
+
elsif update == "1.0.0"
|
40
|
+
m.migration_template 'update_mountain_goat_tables.rb', 'db/migrate', { :migration_file_name => "update_mountain_goat_tables" }
|
41
|
+
end
|
42
|
+
|
35
43
|
end
|
36
44
|
end
|
37
45
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class CreateMountainGoatTables < ActiveRecord::Migration
|
2
2
|
|
3
3
|
def self.up
|
4
|
-
create_table "
|
4
|
+
create_table "mg_ci_metas", :force => true do |t|
|
5
5
|
t.integer "rally_id"
|
6
6
|
t.integer "convert_meta_type_id"
|
7
7
|
t.integer "data"
|
@@ -9,11 +9,11 @@ class CreateMountainGoatTables < ActiveRecord::Migration
|
|
9
9
|
t.datetime "updated_at"
|
10
10
|
end
|
11
11
|
|
12
|
-
add_index "
|
13
|
-
add_index "
|
14
|
-
add_index "
|
15
|
-
|
16
|
-
create_table "
|
12
|
+
add_index "mg_ci_metas", ["convert_meta_type_id", "data"], :name => "ci_metas_cmt_data_index"
|
13
|
+
add_index "mg_ci_metas", ["convert_meta_type_id"], :name => "ci_metas_cmt_index"
|
14
|
+
add_index "mg_ci_metas", ["rally_id"], :name => "ci_metas_rally_index"
|
15
|
+
|
16
|
+
create_table "mg_convert_meta_types", :force => true do |t|
|
17
17
|
t.integer "convert_id"
|
18
18
|
t.string "name"
|
19
19
|
t.string "var"
|
@@ -22,14 +22,16 @@ class CreateMountainGoatTables < ActiveRecord::Migration
|
|
22
22
|
t.datetime "updated_at"
|
23
23
|
end
|
24
24
|
|
25
|
-
create_table "
|
25
|
+
create_table "mg_converts", :force => true do |t|
|
26
26
|
t.string "convert_type"
|
27
27
|
t.datetime "created_at"
|
28
28
|
t.datetime "updated_at"
|
29
29
|
t.string "name"
|
30
|
+
t.float "reward", :default => 1.0
|
31
|
+
t.datetime "deleted_at"
|
30
32
|
end
|
31
33
|
|
32
|
-
create_table "
|
34
|
+
create_table "mg_cs_metas", :force => true do |t|
|
33
35
|
t.integer "rally_id"
|
34
36
|
t.integer "convert_meta_type_id"
|
35
37
|
t.string "data"
|
@@ -37,11 +39,11 @@ class CreateMountainGoatTables < ActiveRecord::Migration
|
|
37
39
|
t.datetime "updated_at"
|
38
40
|
end
|
39
41
|
|
40
|
-
add_index "
|
41
|
-
add_index "
|
42
|
-
add_index "
|
43
|
-
|
44
|
-
create_table "
|
42
|
+
add_index "mg_cs_metas", ["convert_meta_type_id", "data"], :name => "cs_metas_cmt_data_index"
|
43
|
+
add_index "mg_cs_metas", ["convert_meta_type_id"], :name => "cs_metas_cmt_index"
|
44
|
+
add_index "mg_cs_metas", ["rally_id"], :name => "cs_metas_rally_index"
|
45
|
+
|
46
|
+
create_table "mg_metric_variants", :force => true do |t|
|
45
47
|
t.integer "metric_id"
|
46
48
|
t.text "value"
|
47
49
|
t.text "opt1"
|
@@ -51,35 +53,69 @@ class CreateMountainGoatTables < ActiveRecord::Migration
|
|
51
53
|
t.datetime "created_at"
|
52
54
|
t.datetime "updated_at"
|
53
55
|
t.string "name"
|
54
|
-
t.float "priority", :default => 1.0, :null => false
|
55
56
|
t.string "switch_type"
|
57
|
+
t.float "reward"
|
58
|
+
t.datetime "deleted_at"
|
56
59
|
end
|
57
60
|
|
58
|
-
create_table "
|
61
|
+
create_table "mg_metrics", :force => true do |t|
|
59
62
|
t.string "metric_type"
|
60
63
|
t.string "title"
|
61
64
|
t.datetime "created_at"
|
62
65
|
t.datetime "updated_at"
|
63
|
-
t.integer "convert_id"
|
64
66
|
t.boolean "tally_each_serve", :default => true
|
65
67
|
t.boolean "is_switch", :default => false
|
68
|
+
t.datetime "deleted_at"
|
66
69
|
end
|
67
|
-
|
68
|
-
create_table "
|
70
|
+
|
71
|
+
create_table "mg_rallies", :force => true do |t|
|
69
72
|
t.integer "convert_id"
|
70
73
|
t.datetime "created_at"
|
71
74
|
t.datetime "updated_at"
|
72
75
|
end
|
76
|
+
|
77
|
+
create_table "mg_report_items", :force => true do |t|
|
78
|
+
t.integer "report_id"
|
79
|
+
t.integer "reportable_id"
|
80
|
+
t.string "reportable_type"
|
81
|
+
t.integer "order"
|
82
|
+
t.datetime "created_at"
|
83
|
+
t.datetime "updated_at"
|
84
|
+
t.string "pivot_type"
|
85
|
+
t.integer "pivot_id"
|
86
|
+
t.string "filter"
|
87
|
+
t.string "meta_type"
|
88
|
+
t.integer "meta_id"
|
89
|
+
t.string "extra"
|
90
|
+
t.integer "variant"
|
91
|
+
t.integer "subvariant"
|
92
|
+
t.datetime "start"
|
93
|
+
t.datetime "end"
|
94
|
+
t.integer "size"
|
95
|
+
end
|
96
|
+
|
97
|
+
create_table "mg_reports", :force => true do |t|
|
98
|
+
t.string "title"
|
99
|
+
t.text "description"
|
100
|
+
t.datetime "created_at"
|
101
|
+
t.datetime "updated_at"
|
102
|
+
t.string "delivery_set"
|
103
|
+
t.string "recipients"
|
104
|
+
t.datetime "deleted_at"
|
105
|
+
t.string "theme"
|
106
|
+
end
|
73
107
|
end
|
74
108
|
|
75
109
|
def self.down
|
76
|
-
drop_table :
|
77
|
-
drop_table :
|
78
|
-
drop_table :
|
79
|
-
drop_table :
|
80
|
-
drop_table :
|
81
|
-
drop_table :
|
82
|
-
drop_table :
|
110
|
+
drop_table :mg_ci_metas
|
111
|
+
drop_table :mg_convert_meta_types
|
112
|
+
drop_table :mg_converts
|
113
|
+
drop_table :mg_cs_metas
|
114
|
+
drop_table :mg_metric_variants
|
115
|
+
drop_table :mg_metrics
|
116
|
+
drop_table :mg_rallies
|
117
|
+
drop_table :mg_report_items
|
118
|
+
drop_table :mg_reports
|
83
119
|
end
|
84
120
|
|
85
121
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
#Dir["#{Gem.searcher.find('facebooker').full_gem_path}/lib/tasks/*.rake"].each { |ext| load ext }
|
@@ -4,12 +4,31 @@
|
|
4
4
|
#################
|
5
5
|
|
6
6
|
# Add your access passwords for each environment
|
7
|
-
#
|
7
|
+
#
|
8
|
+
# Settings: epsilon for e-greedy strategy
|
9
|
+
# strategy: one of [ 'e-greedy', 'e-greedy-decreasing', 'a/b' ]
|
10
|
+
# storage: ['cookies','session','none']
|
11
|
+
|
12
|
+
# wkhtmltopdf must be installed to use Mountain Goat Reporting
|
13
|
+
# Please specify the executable for wkhtmltopdf below
|
14
|
+
# E.g. (Linux) /usr/local/bin/wkhtmltopdf
|
15
|
+
# (Windows) C:\Program Files (x86)\wkhtmltopdf\wkhtmltopdf.exe
|
16
|
+
# wkhtmltopdf can be installed from http://code.google.com/p/wkhtmltopdf/
|
17
|
+
|
18
|
+
settings:
|
19
|
+
epsilon: 0.1
|
20
|
+
strategy: e-greedy
|
21
|
+
storage: cookies
|
22
|
+
|
8
23
|
test:
|
9
|
-
password: <%= password %>
|
24
|
+
password: <%= password %>
|
25
|
+
wkhtmltopdf: <%= wkhtmltopdf %>
|
10
26
|
development:
|
11
27
|
password: <%= password %>
|
28
|
+
wkhtmltopdf: <%= wkhtmltopdf %>
|
12
29
|
staging:
|
13
30
|
password: <%= password %>
|
31
|
+
wkhtmltopdf: <%= wkhtmltopdf %>
|
14
32
|
production:
|
15
|
-
password: <%= password %>
|
33
|
+
password: <%= password %>
|
34
|
+
wkhtmltopdf: <%= wkhtmltopdf %>
|