i_wonder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. data/MIT-LICENSE +24 -0
  2. data/README.rdoc +147 -0
  3. data/Rakefile +39 -0
  4. data/app/assets/javascripts/i_wonder/application.js +9 -0
  5. data/app/assets/javascripts/i_wonder/dashboard.js +2 -0
  6. data/app/assets/javascripts/i_wonder/events.js +2 -0
  7. data/app/assets/javascripts/i_wonder/metrics.js +19 -0
  8. data/app/assets/javascripts/i_wonder/reports.js +4 -0
  9. data/app/assets/stylesheets/i_wonder/_shared.css.scss +28 -0
  10. data/app/assets/stylesheets/i_wonder/application.css.scss +90 -0
  11. data/app/assets/stylesheets/i_wonder/dashboard.css.scss +6 -0
  12. data/app/assets/stylesheets/i_wonder/events.css.scss +10 -0
  13. data/app/assets/stylesheets/i_wonder/metrics.css.scss +11 -0
  14. data/app/assets/stylesheets/i_wonder/reports.css.scss +19 -0
  15. data/app/controllers/i_wonder/application_controller.rb +4 -0
  16. data/app/controllers/i_wonder/dashboard_controller.rb +9 -0
  17. data/app/controllers/i_wonder/events_controller.rb +14 -0
  18. data/app/controllers/i_wonder/metrics_controller.rb +49 -0
  19. data/app/controllers/i_wonder/reports_controller.rb +53 -0
  20. data/app/helpers/i_wonder/application_helper.rb +4 -0
  21. data/app/helpers/i_wonder/dashboard_helper.rb +4 -0
  22. data/app/helpers/i_wonder/events_helper.rb +4 -0
  23. data/app/helpers/i_wonder/metrics_helper.rb +4 -0
  24. data/app/helpers/i_wonder/reports_helper.rb +4 -0
  25. data/app/models/i_wonder/event.rb +61 -0
  26. data/app/models/i_wonder/metric.rb +223 -0
  27. data/app/models/i_wonder/report.rb +85 -0
  28. data/app/models/i_wonder/report_membership.rb +6 -0
  29. data/app/models/i_wonder/snapshot.rb +32 -0
  30. data/app/views/i_wonder/dashboard/landing.html.erb +5 -0
  31. data/app/views/i_wonder/events/index.html.erb +15 -0
  32. data/app/views/i_wonder/events/show.html.erb +5 -0
  33. data/app/views/i_wonder/metrics/_form.html.erb +66 -0
  34. data/app/views/i_wonder/metrics/_options_for_custom.html.erb +36 -0
  35. data/app/views/i_wonder/metrics/_options_for_event_counter.html.erb +6 -0
  36. data/app/views/i_wonder/metrics/_options_for_model_counter.html.erb +35 -0
  37. data/app/views/i_wonder/metrics/edit.html.erb +7 -0
  38. data/app/views/i_wonder/metrics/index.html.erb +25 -0
  39. data/app/views/i_wonder/metrics/new.html.erb +7 -0
  40. data/app/views/i_wonder/metrics/show.html.erb +28 -0
  41. data/app/views/i_wonder/reports/_form.html.erb +46 -0
  42. data/app/views/i_wonder/reports/_line_report.html.erb +38 -0
  43. data/app/views/i_wonder/reports/edit.html.erb +7 -0
  44. data/app/views/i_wonder/reports/index.html.erb +20 -0
  45. data/app/views/i_wonder/reports/new.html.erb +7 -0
  46. data/app/views/i_wonder/reports/show.html.erb +16 -0
  47. data/app/views/layouts/i_wonder.html.erb +28 -0
  48. data/config/routes.rb +9 -0
  49. data/db/migrate/20111022230720_i_wonder_migrations.rb +84 -0
  50. data/lib/i_wonder/configuration.rb +31 -0
  51. data/lib/i_wonder/core_ext.rb +5 -0
  52. data/lib/i_wonder/engine.rb +23 -0
  53. data/lib/i_wonder/logging/middleware.rb +114 -0
  54. data/lib/i_wonder/logging/mixins/action_controller_mixins.rb +31 -0
  55. data/lib/i_wonder/logging/mixins/active_record_mixins.rb +24 -0
  56. data/lib/i_wonder/version.rb +3 -0
  57. data/lib/i_wonder.rb +18 -0
  58. data/lib/tasks/i_wonder_tasks.rake +8 -0
  59. data/test/dummy/Rakefile +7 -0
  60. data/test/dummy/app/assets/javascripts/application.js +9 -0
  61. data/test/dummy/app/assets/javascripts/highcharts.js +170 -0
  62. data/test/dummy/app/assets/stylesheets/application.css.scss +78 -0
  63. data/test/dummy/app/controllers/application_controller.rb +12 -0
  64. data/test/dummy/app/controllers/test_controller.rb +32 -0
  65. data/test/dummy/app/helpers/application_helper.rb +2 -0
  66. data/test/dummy/app/models/account.rb +3 -0
  67. data/test/dummy/app/views/layouts/application.html.erb +23 -0
  68. data/test/dummy/app/views/test/landing.html.erb +3 -0
  69. data/test/dummy/config/application.rb +45 -0
  70. data/test/dummy/config/boot.rb +10 -0
  71. data/test/dummy/config/database.yml +11 -0
  72. data/test/dummy/config/environment.rb +5 -0
  73. data/test/dummy/config/environments/development.rb +30 -0
  74. data/test/dummy/config/environments/production.rb +60 -0
  75. data/test/dummy/config/environments/test.rb +39 -0
  76. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  77. data/test/dummy/config/initializers/inflections.rb +10 -0
  78. data/test/dummy/config/initializers/mime_types.rb +5 -0
  79. data/test/dummy/config/initializers/secret_token.rb +7 -0
  80. data/test/dummy/config/initializers/session_store.rb +8 -0
  81. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  82. data/test/dummy/config/locales/en.yml +5 -0
  83. data/test/dummy/config/routes.rb +11 -0
  84. data/test/dummy/config.ru +4 -0
  85. data/test/dummy/db/migrate/20111023024745_create_delayed_jobs.rb +21 -0
  86. data/test/dummy/db/migrate/20111023231947_create_accounts.rb +9 -0
  87. data/test/dummy/db/schema.rb +91 -0
  88. data/test/dummy/log/development.log +48142 -0
  89. data/test/dummy/log/test.log +28374 -0
  90. data/test/dummy/public/404.html +26 -0
  91. data/test/dummy/public/422.html +26 -0
  92. data/test/dummy/public/500.html +26 -0
  93. data/test/dummy/public/favicon.ico +0 -0
  94. data/test/dummy/script/delayed_job +5 -0
  95. data/test/dummy/script/rails +6 -0
  96. data/test/dummy/tmp/cache/assets/C41/5B0/sprockets%2F095415e1141a435237aa9464752d21fd +0 -0
  97. data/test/dummy/tmp/cache/assets/C5B/140/sprockets%2F5605473920cc85586d2a795b1e2a7990 +0 -0
  98. data/test/dummy/tmp/cache/assets/C78/210/sprockets%2F26c220614bd9237d671ec621f0428f19 +0 -0
  99. data/test/dummy/tmp/cache/assets/C79/390/sprockets%2F7b9a6409d013f75c40390a26006885ae +0 -0
  100. data/test/dummy/tmp/cache/assets/C81/600/sprockets%2F30b4f9798169b0d0157b48c09ea23472 +0 -0
  101. data/test/dummy/tmp/cache/assets/C94/140/sprockets%2F69a79619df29670102b726d3e7547c6f +0 -0
  102. data/test/dummy/tmp/cache/assets/CA6/380/sprockets%2Fe8e1b47410e6440341afc3903896bc23 +0 -0
  103. data/test/dummy/tmp/cache/assets/CBF/EB0/sprockets%2F279e977af0f392b36126a520569ec7d7 +0 -0
  104. data/test/dummy/tmp/cache/assets/CE5/650/sprockets%2F8ab512c9b33ac90e699b73469d71f061 +0 -0
  105. data/test/dummy/tmp/cache/assets/CE9/220/sprockets%2Fdf40ed258e785336580ff8d3f17504a2 +0 -0
  106. data/test/dummy/tmp/cache/assets/CFE/130/sprockets%2Fa510c5643a4c0c59b74c1746df2c007e +0 -0
  107. data/test/dummy/tmp/cache/assets/D20/120/sprockets%2F73fcb7f2f97642793feb2c3d871c1482 +0 -0
  108. data/test/dummy/tmp/cache/assets/D2B/280/sprockets%2F921f114bc5b7aa015994bdda8421ae15 +0 -0
  109. data/test/dummy/tmp/cache/assets/D2F/9D0/sprockets%2F50ce229bd4200bed47618724bcab6c73 +0 -0
  110. data/test/dummy/tmp/cache/assets/D30/9C0/sprockets%2F3c0162b1955a8f419b14bfc9c21f1bc5 +0 -0
  111. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  112. data/test/dummy/tmp/cache/assets/D38/3B0/sprockets%2Fe28cd7db24b3e4719e539371a331cb3c +0 -0
  113. data/test/dummy/tmp/cache/assets/D3C/510/sprockets%2F59ddac34690f13d66d518c042e5af2a7 +0 -0
  114. data/test/dummy/tmp/cache/assets/D41/880/sprockets%2Fb9fe431461f3a6a728e7c8b57e6201eb +0 -0
  115. data/test/dummy/tmp/cache/assets/D52/330/sprockets%2F575f448badfc9cc6d2618370956d4bf7 +0 -0
  116. data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +0 -0
  117. data/test/dummy/tmp/cache/assets/D58/7E0/sprockets%2Fc3fefb1d72d7614af02a38ac7071c040 +0 -0
  118. data/test/dummy/tmp/cache/assets/D68/240/sprockets%2Fdeadb83d125366600d6a6a69d3f4a19c +0 -0
  119. data/test/dummy/tmp/cache/assets/D6F/B90/sprockets%2F81dd44ced51e0fe8f2615068bb619a5d +0 -0
  120. data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
  121. data/test/dummy/tmp/cache/assets/D8E/250/sprockets%2Fefe57e932e1d26c3b6af42a5311f0a1c +0 -0
  122. data/test/dummy/tmp/cache/assets/D97/740/sprockets%2F9b6d08ad548c1934113d1bcbd8bdc0e9 +0 -0
  123. data/test/dummy/tmp/cache/assets/D9E/120/sprockets%2Fae9efac5ef9a736d5772b07a06723c1a +0 -0
  124. data/test/dummy/tmp/cache/assets/DA3/CD0/sprockets%2Ff4833fc8289edf9e6497c7fe9c19d8d9 +0 -0
  125. data/test/dummy/tmp/cache/assets/DA7/BA0/sprockets%2F463a9dd811f2418d96fa72cfe05fb8cd +0 -0
  126. data/test/dummy/tmp/cache/assets/DAD/040/sprockets%2Fc59fd78ac2642a5f3c1b6ef899d50cf3 +0 -0
  127. data/test/dummy/tmp/cache/assets/DB2/850/sprockets%2F8aca540c22973c1123f4c7b0ebca5ada +0 -0
  128. data/test/dummy/tmp/cache/assets/DBA/4A0/sprockets%2Fffc3120e0bea9134ca5ce8ec74aa2173 +0 -0
  129. data/test/dummy/tmp/cache/assets/DBA/C20/sprockets%2Fa5bfa522bf4221a5bb62ed4feb5093e3 +0 -0
  130. data/test/dummy/tmp/cache/assets/DC7/3C0/sprockets%2F6c54b5103be38cc9ae0ef05c75a92bdf +0 -0
  131. data/test/dummy/tmp/cache/assets/DC8/270/sprockets%2F3ff28a9f19a1be0c377fea3d02e066db +0 -0
  132. data/test/dummy/tmp/cache/assets/DD1/B10/sprockets%2F85b9bce1c5bc3fb86a83d8c8c34e05e0 +0 -0
  133. data/test/dummy/tmp/cache/assets/DDF/BA0/sprockets%2F187ad995e2ff3588c339fdbecceba119 +0 -0
  134. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  135. data/test/dummy/tmp/cache/assets/E8D/B70/sprockets%2Fd62ab1e993c1edcadba5317fe8c7dffb +0 -0
  136. data/test/dummy/tmp/pids/server.pid +1 -0
  137. data/test/factories/metric_factory.rb +13 -0
  138. data/test/factories/report_factory.rb +13 -0
  139. data/test/factories/snapshot_factory.rb +11 -0
  140. data/test/functional/test_controller_test.rb +23 -0
  141. data/test/i_wonder_test.rb +7 -0
  142. data/test/integration/i_wonder/dashboard_controller_test.rb +11 -0
  143. data/test/integration/i_wonder/events_controller_test.rb +14 -0
  144. data/test/integration/i_wonder/metrics_controller_test.rb +18 -0
  145. data/test/integration/i_wonder/reports_controller_test.rb +11 -0
  146. data/test/integration/middleware_test.rb +88 -0
  147. data/test/integration/navigation_test.rb +10 -0
  148. data/test/support/extra_assertions.rb +15 -0
  149. data/test/test_helper.rb +16 -0
  150. data/test/unit/configuration_test.rb +13 -0
  151. data/test/unit/helpers/i_wonder/dashboard_helper_test.rb +6 -0
  152. data/test/unit/helpers/i_wonder/events_helper_test.rb +6 -0
  153. data/test/unit/helpers/i_wonder/metrics_helper_test.rb +6 -0
  154. data/test/unit/helpers/i_wonder/reports_helper_test.rb +6 -0
  155. data/test/unit/i_wonder/event_test.rb +56 -0
  156. data/test/unit/i_wonder/metric_collection_test.rb +124 -0
  157. data/test/unit/i_wonder/metric_creation_test.rb +105 -0
  158. data/test/unit/i_wonder/report_test.rb +33 -0
  159. data/test/unit/i_wonder/snapshot_test.rb +9 -0
  160. 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,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -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,4 @@
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
+ //= require_self
4
+ //= require highcharts
@@ -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,6 @@
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";
@@ -0,0 +1,10 @@
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
+ #event_list {
9
+ li span {margin-right:30px;}
10
+ }
@@ -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,4 @@
1
+ module IWonder
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ module IWonder
2
+ class DashboardController < ApplicationController
3
+ layout "i_wonder"
4
+
5
+ def landing
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ module IWonder
2
+ class EventsController < ApplicationController
3
+ layout "i_wonder"
4
+
5
+ def index
6
+ @groups = Event.groups
7
+ end
8
+
9
+ def show
10
+ @event_details = Event.get_details_for_event_type(params[:id])
11
+ end
12
+
13
+ end
14
+ end
@@ -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,4 @@
1
+ module IWonder
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module IWonder
2
+ module DashboardHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module IWonder
2
+ module EventsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module IWonder
2
+ module MetricsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module IWonder
2
+ module ReportsHelper
3
+ end
4
+ 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