i_wonder 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +24 -0
- data/README.rdoc +147 -0
- data/Rakefile +39 -0
- data/app/assets/javascripts/i_wonder/application.js +9 -0
- data/app/assets/javascripts/i_wonder/dashboard.js +2 -0
- data/app/assets/javascripts/i_wonder/events.js +2 -0
- data/app/assets/javascripts/i_wonder/metrics.js +19 -0
- data/app/assets/javascripts/i_wonder/reports.js +4 -0
- data/app/assets/stylesheets/i_wonder/_shared.css.scss +28 -0
- data/app/assets/stylesheets/i_wonder/application.css.scss +90 -0
- data/app/assets/stylesheets/i_wonder/dashboard.css.scss +6 -0
- data/app/assets/stylesheets/i_wonder/events.css.scss +10 -0
- data/app/assets/stylesheets/i_wonder/metrics.css.scss +11 -0
- data/app/assets/stylesheets/i_wonder/reports.css.scss +19 -0
- data/app/controllers/i_wonder/application_controller.rb +4 -0
- data/app/controllers/i_wonder/dashboard_controller.rb +9 -0
- data/app/controllers/i_wonder/events_controller.rb +14 -0
- data/app/controllers/i_wonder/metrics_controller.rb +49 -0
- data/app/controllers/i_wonder/reports_controller.rb +53 -0
- data/app/helpers/i_wonder/application_helper.rb +4 -0
- data/app/helpers/i_wonder/dashboard_helper.rb +4 -0
- data/app/helpers/i_wonder/events_helper.rb +4 -0
- data/app/helpers/i_wonder/metrics_helper.rb +4 -0
- data/app/helpers/i_wonder/reports_helper.rb +4 -0
- data/app/models/i_wonder/event.rb +61 -0
- data/app/models/i_wonder/metric.rb +223 -0
- data/app/models/i_wonder/report.rb +85 -0
- data/app/models/i_wonder/report_membership.rb +6 -0
- data/app/models/i_wonder/snapshot.rb +32 -0
- data/app/views/i_wonder/dashboard/landing.html.erb +5 -0
- data/app/views/i_wonder/events/index.html.erb +15 -0
- data/app/views/i_wonder/events/show.html.erb +5 -0
- data/app/views/i_wonder/metrics/_form.html.erb +66 -0
- data/app/views/i_wonder/metrics/_options_for_custom.html.erb +36 -0
- data/app/views/i_wonder/metrics/_options_for_event_counter.html.erb +6 -0
- data/app/views/i_wonder/metrics/_options_for_model_counter.html.erb +35 -0
- data/app/views/i_wonder/metrics/edit.html.erb +7 -0
- data/app/views/i_wonder/metrics/index.html.erb +25 -0
- data/app/views/i_wonder/metrics/new.html.erb +7 -0
- data/app/views/i_wonder/metrics/show.html.erb +28 -0
- data/app/views/i_wonder/reports/_form.html.erb +46 -0
- data/app/views/i_wonder/reports/_line_report.html.erb +38 -0
- data/app/views/i_wonder/reports/edit.html.erb +7 -0
- data/app/views/i_wonder/reports/index.html.erb +20 -0
- data/app/views/i_wonder/reports/new.html.erb +7 -0
- data/app/views/i_wonder/reports/show.html.erb +16 -0
- data/app/views/layouts/i_wonder.html.erb +28 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20111022230720_i_wonder_migrations.rb +84 -0
- data/lib/i_wonder/configuration.rb +31 -0
- data/lib/i_wonder/core_ext.rb +5 -0
- data/lib/i_wonder/engine.rb +23 -0
- data/lib/i_wonder/logging/middleware.rb +114 -0
- data/lib/i_wonder/logging/mixins/action_controller_mixins.rb +31 -0
- data/lib/i_wonder/logging/mixins/active_record_mixins.rb +24 -0
- data/lib/i_wonder/version.rb +3 -0
- data/lib/i_wonder.rb +18 -0
- data/lib/tasks/i_wonder_tasks.rake +8 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/javascripts/highcharts.js +170 -0
- data/test/dummy/app/assets/stylesheets/application.css.scss +78 -0
- data/test/dummy/app/controllers/application_controller.rb +12 -0
- data/test/dummy/app/controllers/test_controller.rb +32 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/account.rb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +23 -0
- data/test/dummy/app/views/test/landing.html.erb +3 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +11 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +30 -0
- data/test/dummy/config/environments/production.rb +60 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +11 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20111023024745_create_delayed_jobs.rb +21 -0
- data/test/dummy/db/migrate/20111023231947_create_accounts.rb +9 -0
- data/test/dummy/db/schema.rb +91 -0
- data/test/dummy/log/development.log +48142 -0
- data/test/dummy/log/test.log +28374 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/delayed_job +5 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C41/5B0/sprockets%2F095415e1141a435237aa9464752d21fd +0 -0
- data/test/dummy/tmp/cache/assets/C5B/140/sprockets%2F5605473920cc85586d2a795b1e2a7990 +0 -0
- data/test/dummy/tmp/cache/assets/C78/210/sprockets%2F26c220614bd9237d671ec621f0428f19 +0 -0
- data/test/dummy/tmp/cache/assets/C79/390/sprockets%2F7b9a6409d013f75c40390a26006885ae +0 -0
- data/test/dummy/tmp/cache/assets/C81/600/sprockets%2F30b4f9798169b0d0157b48c09ea23472 +0 -0
- data/test/dummy/tmp/cache/assets/C94/140/sprockets%2F69a79619df29670102b726d3e7547c6f +0 -0
- data/test/dummy/tmp/cache/assets/CA6/380/sprockets%2Fe8e1b47410e6440341afc3903896bc23 +0 -0
- data/test/dummy/tmp/cache/assets/CBF/EB0/sprockets%2F279e977af0f392b36126a520569ec7d7 +0 -0
- data/test/dummy/tmp/cache/assets/CE5/650/sprockets%2F8ab512c9b33ac90e699b73469d71f061 +0 -0
- data/test/dummy/tmp/cache/assets/CE9/220/sprockets%2Fdf40ed258e785336580ff8d3f17504a2 +0 -0
- data/test/dummy/tmp/cache/assets/CFE/130/sprockets%2Fa510c5643a4c0c59b74c1746df2c007e +0 -0
- data/test/dummy/tmp/cache/assets/D20/120/sprockets%2F73fcb7f2f97642793feb2c3d871c1482 +0 -0
- data/test/dummy/tmp/cache/assets/D2B/280/sprockets%2F921f114bc5b7aa015994bdda8421ae15 +0 -0
- data/test/dummy/tmp/cache/assets/D2F/9D0/sprockets%2F50ce229bd4200bed47618724bcab6c73 +0 -0
- data/test/dummy/tmp/cache/assets/D30/9C0/sprockets%2F3c0162b1955a8f419b14bfc9c21f1bc5 +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D38/3B0/sprockets%2Fe28cd7db24b3e4719e539371a331cb3c +0 -0
- data/test/dummy/tmp/cache/assets/D3C/510/sprockets%2F59ddac34690f13d66d518c042e5af2a7 +0 -0
- data/test/dummy/tmp/cache/assets/D41/880/sprockets%2Fb9fe431461f3a6a728e7c8b57e6201eb +0 -0
- data/test/dummy/tmp/cache/assets/D52/330/sprockets%2F575f448badfc9cc6d2618370956d4bf7 +0 -0
- data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +0 -0
- data/test/dummy/tmp/cache/assets/D58/7E0/sprockets%2Fc3fefb1d72d7614af02a38ac7071c040 +0 -0
- data/test/dummy/tmp/cache/assets/D68/240/sprockets%2Fdeadb83d125366600d6a6a69d3f4a19c +0 -0
- data/test/dummy/tmp/cache/assets/D6F/B90/sprockets%2F81dd44ced51e0fe8f2615068bb619a5d +0 -0
- data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
- data/test/dummy/tmp/cache/assets/D8E/250/sprockets%2Fefe57e932e1d26c3b6af42a5311f0a1c +0 -0
- data/test/dummy/tmp/cache/assets/D97/740/sprockets%2F9b6d08ad548c1934113d1bcbd8bdc0e9 +0 -0
- data/test/dummy/tmp/cache/assets/D9E/120/sprockets%2Fae9efac5ef9a736d5772b07a06723c1a +0 -0
- data/test/dummy/tmp/cache/assets/DA3/CD0/sprockets%2Ff4833fc8289edf9e6497c7fe9c19d8d9 +0 -0
- data/test/dummy/tmp/cache/assets/DA7/BA0/sprockets%2F463a9dd811f2418d96fa72cfe05fb8cd +0 -0
- data/test/dummy/tmp/cache/assets/DAD/040/sprockets%2Fc59fd78ac2642a5f3c1b6ef899d50cf3 +0 -0
- data/test/dummy/tmp/cache/assets/DB2/850/sprockets%2F8aca540c22973c1123f4c7b0ebca5ada +0 -0
- data/test/dummy/tmp/cache/assets/DBA/4A0/sprockets%2Fffc3120e0bea9134ca5ce8ec74aa2173 +0 -0
- data/test/dummy/tmp/cache/assets/DBA/C20/sprockets%2Fa5bfa522bf4221a5bb62ed4feb5093e3 +0 -0
- data/test/dummy/tmp/cache/assets/DC7/3C0/sprockets%2F6c54b5103be38cc9ae0ef05c75a92bdf +0 -0
- data/test/dummy/tmp/cache/assets/DC8/270/sprockets%2F3ff28a9f19a1be0c377fea3d02e066db +0 -0
- data/test/dummy/tmp/cache/assets/DD1/B10/sprockets%2F85b9bce1c5bc3fb86a83d8c8c34e05e0 +0 -0
- data/test/dummy/tmp/cache/assets/DDF/BA0/sprockets%2F187ad995e2ff3588c339fdbecceba119 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E8D/B70/sprockets%2Fd62ab1e993c1edcadba5317fe8c7dffb +0 -0
- data/test/dummy/tmp/pids/server.pid +1 -0
- data/test/factories/metric_factory.rb +13 -0
- data/test/factories/report_factory.rb +13 -0
- data/test/factories/snapshot_factory.rb +11 -0
- data/test/functional/test_controller_test.rb +23 -0
- data/test/i_wonder_test.rb +7 -0
- data/test/integration/i_wonder/dashboard_controller_test.rb +11 -0
- data/test/integration/i_wonder/events_controller_test.rb +14 -0
- data/test/integration/i_wonder/metrics_controller_test.rb +18 -0
- data/test/integration/i_wonder/reports_controller_test.rb +11 -0
- data/test/integration/middleware_test.rb +88 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/support/extra_assertions.rb +15 -0
- data/test/test_helper.rb +16 -0
- data/test/unit/configuration_test.rb +13 -0
- data/test/unit/helpers/i_wonder/dashboard_helper_test.rb +6 -0
- data/test/unit/helpers/i_wonder/events_helper_test.rb +6 -0
- data/test/unit/helpers/i_wonder/metrics_helper_test.rb +6 -0
- data/test/unit/helpers/i_wonder/reports_helper_test.rb +6 -0
- data/test/unit/i_wonder/event_test.rb +56 -0
- data/test/unit/i_wonder/metric_collection_test.rb +124 -0
- data/test/unit/i_wonder/metric_creation_test.rb +105 -0
- data/test/unit/i_wonder/report_test.rb +33 -0
- data/test/unit/i_wonder/snapshot_test.rb +9 -0
- metadata +405 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright 2011 OctopusApp Inc. (http://getjobber.com)
|
2
|
+
|
3
|
+
NOTE: This license does not cover the highcharts.js file in the dummy
|
4
|
+
application. Please read the bottom of the README for details.
|
5
|
+
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
= IWonder (Not working yet.)
|
2
|
+
|
3
|
+
Flexible Analytics, and Testing for every corner of your rails application. I read "The Lean Startup" and got inspired. There were a few good solutions, but none of them quite did what I was looking for. I highly recommend you check out Vanity (http://vanity.labnotes.org/) as it is a drop-in solution for many rails apps.
|
4
|
+
|
5
|
+
Key Differences of IWonder:
|
6
|
+
|
7
|
+
- Events get tracked by session, as well as an optional user and/or account. This allows the tracking of a users history.
|
8
|
+
- Tests can be performed against a session (non-user), logged in user, or an entire account. For account based apps, having different versions for different users can be bad.
|
9
|
+
- Data is collected periodically. Lots of useful scenerios for this. (info get's deleted, overwritten, etc)
|
10
|
+
- Different reports (and some metrics) can be setup and changed without having to modify your code.
|
11
|
+
- Different reports can be generated without coding knowledge.
|
12
|
+
- Each hit and visitor comes with a referrer and IP code. Because that events will be transferred to the user when they signup, you can see which campaigns are resulting in actual signups and paid conversions.
|
13
|
+
- Because the session events get added to the user when they log in, you avoid false positives on marketing analytics from visitors returning on different devices. If your application has a mobile interface, this basically avoids all your users being logged as visitors twice.
|
14
|
+
|
15
|
+
More big plans!
|
16
|
+
|
17
|
+
- Long term campaign tracking interface (the events already get logged, but this will make it easier to see results)
|
18
|
+
- E-mail open tracking (for application generated e-mails like the 'Welcome E-mail')
|
19
|
+
- Feature request buttons. - details coming soon
|
20
|
+
|
21
|
+
== Requirements
|
22
|
+
|
23
|
+
* Due to performance optimization IWonder must be used with an SQL style DB currently.
|
24
|
+
* Rails 3.1 or higher
|
25
|
+
* Ability to run hourly rake tasks (rake cron)
|
26
|
+
* Ability to run a background worker process (delayed_job)
|
27
|
+
|
28
|
+
== Installation
|
29
|
+
|
30
|
+
=== Install the Gem
|
31
|
+
|
32
|
+
Add to your <code>Gemfile</code>:
|
33
|
+
|
34
|
+
gem "i_wonder"
|
35
|
+
|
36
|
+
and install:
|
37
|
+
|
38
|
+
bundle install
|
39
|
+
|
40
|
+
=== Migrate the DB
|
41
|
+
|
42
|
+
Generate the migration files and migrate your database:
|
43
|
+
|
44
|
+
rake i_wonder:install:migrations
|
45
|
+
rake db:migrate
|
46
|
+
|
47
|
+
|
48
|
+
=== Create the dashboard controller
|
49
|
+
|
50
|
+
Add the engine to your routes. This is where the dashboard will be hosted
|
51
|
+
|
52
|
+
Rails.application.routes.draw do
|
53
|
+
...
|
54
|
+
mount IWonder::Engine => "/super_analytics_engine"
|
55
|
+
end
|
56
|
+
|
57
|
+
This engine will use your standard application layout (unless you set a different layout in the configurations explained later). Whichever layout you use, you
|
58
|
+
must include a yield in the HTML header so the CSS and Javascript can be loaded.
|
59
|
+
|
60
|
+
<%= yield :extra_scripts_and_styles %>
|
61
|
+
|
62
|
+
This engine also assumes that your layout will handle the flash notifications.
|
63
|
+
|
64
|
+
===== Example application layout:
|
65
|
+
|
66
|
+
<html>
|
67
|
+
<head>
|
68
|
+
|
69
|
+
... your javascript/css/meta/header stuff here ...
|
70
|
+
|
71
|
+
<%= yield :extra_scripts_and_styles %>
|
72
|
+
</head>
|
73
|
+
<body>
|
74
|
+
<%= notifications %>
|
75
|
+
<%= yield %>
|
76
|
+
</body>
|
77
|
+
</html
|
78
|
+
|
79
|
+
|
80
|
+
=== Setting up the hourly cron task
|
81
|
+
|
82
|
+
This gem hooks into the "<code>rake cron</code>" request. It can't take snapshots of your metrics any faster than this cron is run. We recommend every hour.
|
83
|
+
This will be run automatically if you are hosted on Heroku and have the 'hourly cron' add-on enabled.
|
84
|
+
|
85
|
+
|
86
|
+
=== Install Dependecies
|
87
|
+
|
88
|
+
This Gem requires the Delayed::Job gem. Installation and configuration instructions can be found at http://github.com/tobi/delayed_job.
|
89
|
+
|
90
|
+
We also require the highcharts javascript file in your applications asset path. We don't include it because of licencing reasons. It's free for testing and development. You can find it at http://www.highcharts.com/
|
91
|
+
|
92
|
+
|
93
|
+
=== Security
|
94
|
+
|
95
|
+
IWonder's controllers will run any <code>:before_filters</code> you have set in your application controller. You can easily check for a password or admin status there.
|
96
|
+
|
97
|
+
=== Options & Configurations
|
98
|
+
|
99
|
+
You can choose to navigate directly to the dashboard. If you want to include a link on your admin page to the i_wonder dashboard, your can access the url with <code>i_wonder.root_url</code> such as:
|
100
|
+
|
101
|
+
<%= link_to "Go to analytics >", i_wonder.root_url %>
|
102
|
+
|
103
|
+
|
104
|
+
If you want to configure this gem, add this to an initializer.
|
105
|
+
|
106
|
+
IWonder.configuration do |c|
|
107
|
+
c.controllers_to_ignore = [] # These are all the controllers which don't log any events (i_wonder is also ignored)
|
108
|
+
c.only_log_hits_on_200 = true # By default hits and new visitors won't be logged on anything but a 200 or 304 (not modified). Your custom events will still be recorded.
|
109
|
+
c.back_to_app_link = "/" # this is the link hitting the home button in IWonder will take you to.
|
110
|
+
c.app_name = "My App"
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
== Usage
|
115
|
+
|
116
|
+
=== Event tracking
|
117
|
+
|
118
|
+
==== Setting session, user, and account details
|
119
|
+
|
120
|
+
This gem will track the visitor with a permanent cookie (will remember them next visit). However, it can also track users and accounts. Simply call one or both of these methods anywhere in the controller. When a user get's added part way through, the :user_id will get filled in for all the previous events for that session. The :new_visitor event for the session will also be removed if the user has an earlier dated :new_visitor event on their account.
|
121
|
+
|
122
|
+
i_wonder_for_user_id(user_id)
|
123
|
+
|
124
|
+
or
|
125
|
+
|
126
|
+
i_wonder_for_account_id(account_id)
|
127
|
+
|
128
|
+
:hits and :new_visitors will be logged automatically. You can store any additional by calling <code>report!(event_sym, options = {})</code> in the controller.
|
129
|
+
You can also log an event from a model with the same method <code>report!(event_sym, options = {})</code>, but you will need to provide the <code>:user_id</code>
|
130
|
+
or <code>:account_id</code> in the options, if you want them recorded.
|
131
|
+
|
132
|
+
|
133
|
+
== Contributing & Reporting Issues
|
134
|
+
|
135
|
+
In progress...
|
136
|
+
|
137
|
+
|
138
|
+
== Licensing
|
139
|
+
|
140
|
+
This application is released under the MIT license.
|
141
|
+
|
142
|
+
However, in the dummy app for testing purposes, it contains the Highcharts javascript library (highcharts.com) which is released under the "Creative Commons Attribution-NonCommercial 3.0 License". It is free for testing and development, but not for commercial use. Please see the details at http://highcharts.com/license
|
143
|
+
|
144
|
+
---
|
145
|
+
|
146
|
+
|
147
|
+
Copyright on Updates - Copyright (c) 2011 OctopusApp Inc. ({getjobber.com}[http://getjobber.com]), released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'IWonder'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
|
27
|
+
Bundler::GemHelper.install_tasks
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
|
31
|
+
Rake::TestTask.new(:test) do |t|
|
32
|
+
t.libs << 'lib'
|
33
|
+
t.libs << 'test'
|
34
|
+
t.pattern = 'test/**/*_test.rb'
|
35
|
+
t.verbose = false
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
task :default => :test
|
@@ -0,0 +1,9 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into including all the files listed below.
|
2
|
+
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
3
|
+
// be included in the compiled file accessible from http://example.com/assets/application.js
|
4
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
5
|
+
// the compiled file.
|
6
|
+
//
|
7
|
+
//= require jquery
|
8
|
+
//= require jquery_ujs
|
9
|
+
//= require_tree .
|
@@ -0,0 +1,19 @@
|
|
1
|
+
// Place all the behaviors and hooks related to the matching controller here.
|
2
|
+
// All this logic will automatically be available in application.js.
|
3
|
+
|
4
|
+
|
5
|
+
$(function(){
|
6
|
+
$("#metric_collection_type").change(function(){
|
7
|
+
$(".details_for_option").appendTo($("#unused_form_elements"));
|
8
|
+
|
9
|
+
$("."+$(this).val()+"_details").appendTo($("#active_options_holder"));
|
10
|
+
});
|
11
|
+
|
12
|
+
$("#metric_takes_snapshots").change(function(){
|
13
|
+
if($(this).prop('checked')){
|
14
|
+
$(".snapshot_details").show();
|
15
|
+
}else{
|
16
|
+
$(".snapshot_details").hide();
|
17
|
+
}
|
18
|
+
});
|
19
|
+
})
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
$light_blue_box_background: #F8F8F8;
|
3
|
+
$light_blue_box_border: #E9E9E9;
|
4
|
+
$dark_red: #af0000;
|
5
|
+
$pale_red: #ffd2d2;
|
6
|
+
|
7
|
+
@mixin rounded_corners($radius: 5px) { -moz-border-radius: $radius; -webkit-border-radius: $radius; border-radius: $radius; position:relative;}
|
8
|
+
|
9
|
+
|
10
|
+
@mixin shadow($x_offset: 0px, $y_offset: 1px, $size: 8px, $color: rgba(0, 0, 0, 0.5)) {
|
11
|
+
-webkit-box-shadow:$x_offset $y_offset $size $color;
|
12
|
+
-moz-box-shadow:$x_offset $y_offset $size $color;
|
13
|
+
box-shadow:$x_offset $y_offset $size $color;
|
14
|
+
}
|
15
|
+
|
16
|
+
@mixin gradiant ($color_a:#aaa, $color_b:#bbb) {
|
17
|
+
position:relative;
|
18
|
+
background-image: -webkit-linear-gradient(top, $color_a, $color_b); /* Chrome 10+, Saf5.1+ */
|
19
|
+
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from($color_a), to($color_b)); /* Saf4+, Chrome */
|
20
|
+
background-image: -moz-linear-gradient(top,$color_a,$color_b); /* FF3.6 */
|
21
|
+
background-image: -ms-linear-gradient(top, $color_a, $color_b); /* IE10 */
|
22
|
+
background-image: -o-linear-gradient(top, $color_a, $color_b); /* Opera 11.10+ */
|
23
|
+
}
|
24
|
+
|
25
|
+
@mixin quick_grad ($main_color:#aaa, $buldge:0.2) {
|
26
|
+
background-color:$main_color;
|
27
|
+
@include gradiant($main_color*(1 + $buldge), $main_color*(1 - $buldge));
|
28
|
+
}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
3
|
+
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
4
|
+
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
5
|
+
*= require_self
|
6
|
+
*= require i_wonder/dashboard
|
7
|
+
*= require i_wonder/events
|
8
|
+
*= require i_wonder/reports
|
9
|
+
*= require i_wonder/metrics
|
10
|
+
*/
|
11
|
+
|
12
|
+
@import "_shared.css.scss";
|
13
|
+
|
14
|
+
html, body { background-color: #e5e5e5; font-family: Verdana, Helvetica, Arial; font-size: 14px; }
|
15
|
+
|
16
|
+
.flash { padding: 5px 8px; margin: 10px 0; @include rounded_corners(3px);
|
17
|
+
&.flash_notice { background-color: #CFC; border: solid 1px #6C6; }
|
18
|
+
&.flash_warning { background-color: #FFFFE0; border: solid 1px #ffa500; }
|
19
|
+
&.flash_error, &.flash_alert { background-color:$pale_red; border: solid 1px$dark_red; }
|
20
|
+
}
|
21
|
+
|
22
|
+
#i_wonder_wrapper {width:75%; margin: 20px auto 0 auto; @include shadow(); @include rounded_corners(4px); overflow:hidden; background-color: #FFF;
|
23
|
+
|
24
|
+
nav {background-color:#777; @include quick_grad(#777, 0.2); text-align:right; @include rounded_corners(4px);
|
25
|
+
a, span { display:block; padding:5px 10px;}
|
26
|
+
a { font-weight:bold; color:#eef; float:left; text-decoration:none; border-right:1px solid #222; border-left:1px solid #778; text-shadow:-1px -1px 1px #444;
|
27
|
+
&:hover {color:#bbb; text-shadow:1px 1px 1px #333;}
|
28
|
+
}
|
29
|
+
span { color:#234;}
|
30
|
+
}
|
31
|
+
#container { padding: 20px 40px; }
|
32
|
+
|
33
|
+
p.empty_list {color:#666; }
|
34
|
+
|
35
|
+
ul.primary_list { padding:0px; margin:10px 0; border:1px solid $light_blue_box_border; border-bottom:none; background-color:$light_blue_box_background;
|
36
|
+
li { list-style:none; padding:0px; margin:0px; border-bottom:1px solid $light_blue_box_border;
|
37
|
+
a {display:block; padding:5px; text-decoration:none; height:40px;
|
38
|
+
h3 {padding:0px; margin:0 0 10px 0; font-size:110%; color:#000; font-weight:normal;
|
39
|
+
span {color:#555; font-weight:normal;}
|
40
|
+
}
|
41
|
+
p {padding:0px; margin:0px; font-size:85%; color:#333;}
|
42
|
+
&:hover { background-color:#eef; color:black;
|
43
|
+
* {text-decoration:none; color:blue;}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
.show {
|
50
|
+
p.description { font-size:90%; color:#555;}
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
form.main_form { background-color:$light_blue_box_background; border:1px solid $light_blue_box_border; padding:10px 15px; display:inline-block;
|
55
|
+
h3 {margin:0 0 10px 0;}
|
56
|
+
.field { margin:20px 0 20px 0;
|
57
|
+
label {font-weight:bold; color:#333; font-size:90%; margin-bottom:5px; display:block;}
|
58
|
+
input, textarea, select {margin-left:0px; clear:left;}
|
59
|
+
input[type=checkbox] {float:left; margin-right:5px;}
|
60
|
+
input[type=submit] {font-size:120%; font-weight:bold;}
|
61
|
+
span.description {color:#555; font-size:80%;}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
form .field_with_errors { display:inline;
|
66
|
+
label { color:red; }
|
67
|
+
input { color:red; border-color:red;}
|
68
|
+
}
|
69
|
+
|
70
|
+
#error_explanation {
|
71
|
+
width: 450px;
|
72
|
+
border: 2px solid red;
|
73
|
+
padding: 7px;
|
74
|
+
padding-bottom: 0;
|
75
|
+
margin-bottom: 20px;
|
76
|
+
background-color: #f0f0f0;
|
77
|
+
h2 {
|
78
|
+
text-align: left;
|
79
|
+
font-weight: bold;
|
80
|
+
padding: 5px 5px 5px 15px;
|
81
|
+
font-size: 12px;
|
82
|
+
margin: -7px;
|
83
|
+
margin-bottom: 0px;
|
84
|
+
background-color: #c00;
|
85
|
+
color: #fff; }
|
86
|
+
ul li {
|
87
|
+
font-size: 12px;
|
88
|
+
list-style: square; } }
|
89
|
+
|
90
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
/*
|
2
|
+
Place all the styles related to the matching controller here.
|
3
|
+
They will automatically be included in application.css.
|
4
|
+
*/
|
5
|
+
|
6
|
+
@import "_shared.css.scss";
|
7
|
+
|
8
|
+
|
9
|
+
pre { padding: 20px; border:1px solid $light_blue_box_border; background-color:$light_blue_box_background; text-align:left; display:inline-block;}
|
10
|
+
|
11
|
+
#unused_form_elements {display:none;}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
/*
|
2
|
+
Place all the styles related to the matching controller here.
|
3
|
+
They will automatically be included in application.css.
|
4
|
+
*/
|
5
|
+
|
6
|
+
@import "_shared.css.scss";
|
7
|
+
|
8
|
+
form.report {
|
9
|
+
|
10
|
+
#new_metric_button {float:right;}
|
11
|
+
.nested_metric_form {border:1px solid $light_blue_box_border; padding:5px; margin:5px 0; background-color:#fff;
|
12
|
+
label {width:95px;}
|
13
|
+
textarea {margin:10px 0 10px 20px;}
|
14
|
+
}
|
15
|
+
|
16
|
+
.report_metric_ids { padding:0px; margin:10px 0;
|
17
|
+
li {padding:0px; margin:0px; list-style:none;}
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module IWonder
|
2
|
+
class MetricsController < ApplicationController
|
3
|
+
layout "i_wonder"
|
4
|
+
|
5
|
+
def index
|
6
|
+
@metrics = Metric.all
|
7
|
+
end
|
8
|
+
|
9
|
+
def show
|
10
|
+
@metric = Metric.find(params[:id])
|
11
|
+
end
|
12
|
+
|
13
|
+
def new
|
14
|
+
@metric = Metric.new(params[:metric])
|
15
|
+
end
|
16
|
+
|
17
|
+
def create
|
18
|
+
@metric = Metric.new(params[:metric])
|
19
|
+
|
20
|
+
if @metric.save
|
21
|
+
redirect_to @metric, :notice => "Successfully created metric"
|
22
|
+
else
|
23
|
+
render "new"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def edit
|
28
|
+
@metric = Metric.find(params[:id])
|
29
|
+
render "edit"
|
30
|
+
end
|
31
|
+
|
32
|
+
def update
|
33
|
+
@metric = Metric.find(params[:id])
|
34
|
+
|
35
|
+
if @metric.update_attributes(params[:metric])
|
36
|
+
redirect_to @metric, :notice => "Successfully updated metric"
|
37
|
+
else
|
38
|
+
render "edit"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def destroy
|
43
|
+
@metric = Metric.find(params[:id])
|
44
|
+
@metric.destroy
|
45
|
+
redirect_to metrics_path, :notice => "Metric has been destroyed"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module IWonder
|
2
|
+
class ReportsController < ApplicationController
|
3
|
+
layout "i_wonder"
|
4
|
+
|
5
|
+
def index
|
6
|
+
@reports = Report.all
|
7
|
+
end
|
8
|
+
|
9
|
+
def show
|
10
|
+
@report = Report.find(params[:id])
|
11
|
+
|
12
|
+
@start_time = Time.zone.now - 30.hours #(params[:start_time] || @report.default_start_time)
|
13
|
+
@end_time = (params[:end_time] || Time.zone.now)
|
14
|
+
@interval_length = 1.hour
|
15
|
+
end
|
16
|
+
|
17
|
+
def new
|
18
|
+
@report = Report.new(params[:report])
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
@report = Report.new(params[:report])
|
23
|
+
|
24
|
+
if @report.save
|
25
|
+
redirect_to @report, :notice => "Successfully created report"
|
26
|
+
else
|
27
|
+
render "new"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def edit
|
32
|
+
@report = Report.find(params[:id])
|
33
|
+
render "edit"
|
34
|
+
end
|
35
|
+
|
36
|
+
def update
|
37
|
+
@report = Report.find(params[:id])
|
38
|
+
|
39
|
+
if @report.update_attributes(params[:report])
|
40
|
+
redirect_to @report, :notice => "Successfully updated report"
|
41
|
+
else
|
42
|
+
render "edit"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def destroy
|
47
|
+
@report = Report.find(params[:id])
|
48
|
+
@report.destroy
|
49
|
+
redirect_to reports_path, :notice => "Report has been destroyed"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module IWonder
|
2
|
+
class Event < ActiveRecord::Base
|
3
|
+
attr_accessible :event_type, :account_id, :user_id, :session_id, :controller, :action, :referrer, :user_agent, :remote_ip
|
4
|
+
serialize :extra_details, Hash
|
5
|
+
|
6
|
+
validates_presence_of :event_type, :on => :create, :message => "can't be blank"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def merge_session_to_user(session_id, user_id)
|
10
|
+
update_all("user_id = '#{user_id}'", "session_id = '#{session_id}' AND user_id IS NULL")
|
11
|
+
|
12
|
+
# TODO: remove all, but the earliest new_visit event
|
13
|
+
end
|
14
|
+
# handle_asynchronously :merge_session_to_user
|
15
|
+
|
16
|
+
|
17
|
+
def fast_create(attr_hash)
|
18
|
+
attr_hash[:created_at] = Time.now.utc
|
19
|
+
attr_hash[:updated_at] = Time.now.utc
|
20
|
+
|
21
|
+
keys = [:event_type, :account_id, :user_id, :session_id, :controller, :action, :extra_details, :remote_ip, :user_agent, :referrer, :created_at, :updated_at]
|
22
|
+
key_str = keys.collect(&:to_s).join(", ")
|
23
|
+
|
24
|
+
pre_value = keys.collect{ "?" }.join(", ")
|
25
|
+
values_array = keys.collect{|k|
|
26
|
+
v = attr_hash[k]
|
27
|
+
v = v.delete_blank.to_yaml if v.is_a?(Hash)
|
28
|
+
v
|
29
|
+
}
|
30
|
+
value_str = sanitize_sql_array( [pre_value] + values_array)
|
31
|
+
|
32
|
+
Event.connection.execute "INSERT INTO #{table_name} (#{key_str}) values (#{value_str})"
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns an array of hashes. Each one has a {:event_type, :count, :most_recent} and is sorted by decending most_recent
|
36
|
+
def groups
|
37
|
+
Event.select("event_type, COUNT(event_type) as count, MAX(created_at) as most_recent").group("event_type").order("most_recent DESC").collect{|e|
|
38
|
+
{
|
39
|
+
:event_type => e["event_type"],
|
40
|
+
:count => e["count"].to_i,
|
41
|
+
:most_recent => Time.parse(e["most_recent"])
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def get_details_for_event_type(type)
|
48
|
+
Event.where(:event_type => type).select("COUNT(event_type) as count, MAX(created_at) as most_recent").group("event_type").collect{|e|
|
49
|
+
{
|
50
|
+
:event_type => type,
|
51
|
+
:count => e["count"].to_i,
|
52
|
+
:most_recent => Time.parse(e["most_recent"])
|
53
|
+
}
|
54
|
+
}.first
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|