prosperity 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/prosperity/application.js.coffee +5 -24
  3. data/app/assets/javascripts/prosperity/dashboards.js.coffee +17 -0
  4. data/app/assets/javascripts/prosperity/graph.js.coffee +89 -0
  5. data/app/assets/stylesheets/prosperity/application.css.scss +1 -1
  6. data/app/assets/stylesheets/prosperity/dashboards.css +4 -0
  7. data/app/assets/stylesheets/prosperity/graph.css +4 -0
  8. data/app/controllers/prosperity/application_controller.rb +30 -0
  9. data/app/controllers/prosperity/dashboard_graphs_controller.rb +24 -0
  10. data/app/controllers/prosperity/dashboards_controller.rb +33 -0
  11. data/app/controllers/prosperity/graphs_controller.rb +72 -0
  12. data/app/controllers/prosperity/metrics_controller.rb +57 -0
  13. data/app/helpers/prosperity/application_helper.rb +4 -0
  14. data/app/helpers/prosperity/dashboard_graphs_helper.rb +4 -0
  15. data/app/helpers/prosperity/dashboards_helper.rb +4 -0
  16. data/app/helpers/prosperity/graph_helper.rb +7 -0
  17. data/app/models/prosperity/dashboard.rb +8 -0
  18. data/app/models/prosperity/dashboard_graph.rb +6 -0
  19. data/app/models/prosperity/graph.rb +20 -0
  20. data/app/models/prosperity/graph_line.rb +8 -0
  21. data/app/views/layouts/prosperity/application.html.erb +56 -0
  22. data/app/views/prosperity/dashboards/edit.html.erb +24 -0
  23. data/app/views/prosperity/dashboards/index.html.erb +15 -0
  24. data/app/views/prosperity/dashboards/new.html.erb +8 -0
  25. data/app/views/prosperity/dashboards/show.html.erb +14 -0
  26. data/app/views/prosperity/graphs/edit.html.erb +41 -0
  27. data/app/views/prosperity/graphs/new.html.erb +15 -0
  28. data/app/views/prosperity/metrics/index.html.erb +8 -0
  29. data/app/views/prosperity/metrics/show.html.erb +23 -0
  30. data/app/views/prosperity/shared/_date_range.html.erb +9 -0
  31. data/config/routes.rb +12 -2
  32. data/db/migrate/20131127042251_dashboards.rb +34 -0
  33. data/lib/generators/metric/USAGE +8 -0
  34. data/lib/generators/metric/metric_generator.rb +7 -0
  35. data/lib/generators/metric/templates/metric.rb.erb +3 -0
  36. data/lib/prosperity/aggregate/aggregate_builder.rb +42 -0
  37. data/lib/prosperity/aggregate/average.rb +12 -0
  38. data/lib/prosperity/aggregate/base.rb +5 -0
  39. data/lib/prosperity/aggregate/count.rb +12 -0
  40. data/lib/prosperity/aggregate/maximum.rb +12 -0
  41. data/lib/prosperity/aggregate/minimum.rb +12 -0
  42. data/lib/prosperity/aggregate/sql.rb +12 -0
  43. data/lib/prosperity/aggregate/sum.rb +12 -0
  44. data/lib/prosperity/aggregate/with_column.rb +10 -0
  45. data/lib/prosperity/aggregate.rb +5 -0
  46. data/lib/prosperity/engine.rb +14 -0
  47. data/lib/prosperity/exception.rb +19 -1
  48. data/lib/prosperity/extractors/base.rb +33 -5
  49. data/lib/prosperity/extractors/change.rb +35 -0
  50. data/lib/prosperity/extractors/interval.rb +51 -0
  51. data/lib/prosperity/extractors/total.rb +23 -0
  52. data/lib/prosperity/helpers/time.rb +12 -0
  53. data/lib/prosperity/metric.rb +90 -5
  54. data/lib/prosperity/metric_finder.rb +10 -0
  55. data/lib/prosperity/metrics/option.rb +2 -0
  56. data/lib/prosperity/period.rb +4 -3
  57. data/lib/prosperity/periods.rb +13 -2
  58. data/lib/prosperity/version.rb +1 -1
  59. data/spec/controllers/prosperity/dashboard_graphs_controller_spec.rb +45 -0
  60. data/spec/controllers/prosperity/dashboards_controller_spec.rb +54 -0
  61. data/spec/controllers/prosperity/graphs_controller_spec.rb +145 -0
  62. data/spec/controllers/prosperity/metrics_controller_spec.rb +78 -0
  63. data/spec/dummy/app/models/user.rb +1 -0
  64. data/spec/dummy/app/prosperity/user_value_sum_metric.rb +4 -0
  65. data/spec/dummy/app/prosperity/user_value_sum_sql_metric.rb +4 -0
  66. data/spec/dummy/app/prosperity/users_metric.rb +8 -0
  67. data/spec/dummy/app/prosperity/users_sql_metric.rb +4 -0
  68. data/spec/dummy/config/database.yml +0 -8
  69. data/spec/dummy/config/routes.rb +1 -1
  70. data/spec/dummy/db/migrate/20140217005117_add_value_to_users.rb +5 -0
  71. data/spec/dummy/db/schema.rb +37 -1
  72. data/spec/dummy/db/seeds.rb +3 -1
  73. data/spec/dummy/log/development.log +76950 -0
  74. data/spec/dummy/log/test.log +111316 -0
  75. data/spec/dummy/tmp/cache/assets/development/sass/34a577735054231563e7022ea73e1468db9203ba/application.css.scssc +0 -0
  76. data/spec/dummy/tmp/cache/assets/development/sprockets/07e8fb2db7d85e29590a6b05160ca825 +0 -0
  77. data/spec/dummy/tmp/cache/assets/development/sprockets/1533bcf4a8f5a745dfda80cebd88d217 +0 -0
  78. data/spec/dummy/tmp/cache/assets/development/sprockets/1643e7f9f85637df140a850af6674f10 +0 -0
  79. data/spec/dummy/tmp/cache/assets/development/sprockets/197492ed379339e17a0f5d01d3b01b8c +0 -0
  80. data/spec/dummy/tmp/cache/assets/development/sprockets/1c55cf24465f311353197ce336df0178 +0 -0
  81. data/spec/dummy/tmp/cache/assets/development/sprockets/210050da208fb75a75b701bfa4e8470f +0 -0
  82. data/spec/dummy/tmp/cache/assets/development/sprockets/2733033a48e2e8a6fdd6e4ab371689ee +0 -0
  83. data/spec/dummy/tmp/cache/assets/development/sprockets/27a59e08207d3ae723f2b2b3d22264c3 +0 -0
  84. data/spec/dummy/tmp/cache/assets/development/sprockets/3e21790900d62ae3954585f08a1cb28a +0 -0
  85. data/spec/dummy/tmp/cache/assets/development/sprockets/3f76425644a701b85db3d385f077052d +0 -0
  86. data/spec/dummy/tmp/cache/assets/development/sprockets/448c1e44da04c20f27eaa31ad8c4391e +0 -0
  87. data/spec/dummy/tmp/cache/assets/development/sprockets/47df4fdca8a2ff6a43c18d7aa1ffd9c5 +0 -0
  88. data/spec/dummy/tmp/cache/assets/development/sprockets/566fbede2036fd06a20e7e52e30ddc7d +0 -0
  89. data/spec/dummy/tmp/cache/assets/development/sprockets/5f1aaf22720701db690538726e896a39 +0 -0
  90. data/spec/dummy/tmp/cache/assets/development/sprockets/647038c58f26163a9217646e1e5f09ae +0 -0
  91. data/spec/dummy/tmp/cache/assets/development/sprockets/6882b260ae69f1594eff540d803e5ac3 +0 -0
  92. data/spec/dummy/tmp/cache/assets/development/sprockets/69f4b83d363269ee4122bbbaaa1325d1 +0 -0
  93. data/spec/dummy/tmp/cache/assets/development/sprockets/6e1aee34907ba1ccdbfca2deb18a5adb +0 -0
  94. data/spec/dummy/tmp/cache/assets/development/sprockets/72c0d4a0eeedae5e79ae49abb24f6d32 +0 -0
  95. data/spec/dummy/tmp/cache/assets/development/sprockets/828a046f0e7dc2dad0eecfe49d31e14d +0 -0
  96. data/spec/dummy/tmp/cache/assets/development/sprockets/91ae0bd43f98b4f1f111e0bdc178302c +0 -0
  97. data/spec/dummy/tmp/cache/assets/development/sprockets/97d99b466bedb69706f4815fa26b69f3 +0 -0
  98. data/spec/dummy/tmp/cache/assets/development/sprockets/990cf740a7968767a327f6186313badb +0 -0
  99. data/spec/dummy/tmp/cache/assets/development/sprockets/a2b14d5c46db32da9183354fb3fcd0bf +0 -0
  100. data/spec/dummy/tmp/cache/assets/development/sprockets/a6d6196cfd275dea0718553a95f997a5 +0 -0
  101. data/spec/dummy/tmp/cache/assets/development/sprockets/bb1f5ffb0ccbeec2c6499044b356f7c6 +0 -0
  102. data/spec/dummy/tmp/cache/assets/development/sprockets/bc83340720a76d8208112f6aecbff08e +0 -0
  103. data/spec/dummy/tmp/cache/assets/development/sprockets/be67cef79122a71f7432c2f984f1f258 +0 -0
  104. data/spec/dummy/tmp/cache/assets/development/sprockets/c83e71ece967e80f8df838874c130431 +0 -0
  105. data/spec/dummy/tmp/cache/assets/development/sprockets/cad26971ff72bbf5aa5f4e6a469389eb +0 -0
  106. data/spec/dummy/tmp/cache/assets/development/sprockets/d0d5517256edcd1f83b11a13c6ca040f +0 -0
  107. data/spec/dummy/tmp/cache/assets/development/sprockets/d0f0c5e105bfb1d6e5cd7295fbd1f65d +0 -0
  108. data/spec/dummy/tmp/cache/assets/development/sprockets/d3087f7df919b7f0d8142ee163db6c79 +0 -0
  109. data/spec/dummy/tmp/cache/assets/development/sprockets/decc27a1fa9ea32fe0dcc18619e32587 +0 -0
  110. data/spec/dummy/tmp/cache/assets/development/sprockets/df796f7ea525be06a82ebc3a4f97ffec +0 -0
  111. data/spec/dummy/tmp/cache/assets/development/sprockets/e02b5049cf477b43a72b4a00e2fceb46 +0 -0
  112. data/spec/dummy/tmp/cache/assets/development/sprockets/e2a9bdd87a2d19256837fce6544a122c +0 -0
  113. data/spec/dummy/tmp/cache/assets/development/sprockets/e378dcebf3c529db0d6e57d0c8dc3904 +0 -0
  114. data/spec/dummy/tmp/cache/assets/development/sprockets/ee3031bd54230ddf2addb5fa5718c86b +0 -0
  115. data/spec/dummy/tmp/cache/assets/development/sprockets/f4648036e284d9aa3468f4f7b9bbb967 +0 -0
  116. data/spec/dummy/tmp/cache/assets/development/sprockets/f9bbf698d099ead559e5ba4a5a4c5538 +0 -0
  117. data/spec/dummy/tmp/cache/assets/development/sprockets/fb8d3825617fcc11dfa614a51effee0e +0 -0
  118. data/spec/dummy/tmp/cache/assets/test/sass/34a577735054231563e7022ea73e1468db9203ba/application.css.scssc +0 -0
  119. data/spec/dummy/tmp/cache/assets/test/sprockets/07e8fb2db7d85e29590a6b05160ca825 +0 -0
  120. data/spec/dummy/tmp/cache/assets/test/sprockets/0a40fa5e0d6c0de40a8a698dcd697305 +0 -0
  121. data/spec/dummy/tmp/cache/assets/test/sprockets/1533bcf4a8f5a745dfda80cebd88d217 +0 -0
  122. data/spec/dummy/tmp/cache/assets/test/sprockets/1643e7f9f85637df140a850af6674f10 +0 -0
  123. data/spec/dummy/tmp/cache/assets/test/sprockets/177ca1f3e92db4f8a8f6e7ad9786ebfc +0 -0
  124. data/spec/dummy/tmp/cache/assets/test/sprockets/197492ed379339e17a0f5d01d3b01b8c +0 -0
  125. data/spec/dummy/tmp/cache/assets/test/sprockets/1c55cf24465f311353197ce336df0178 +0 -0
  126. data/spec/dummy/tmp/cache/assets/test/sprockets/1cb94582c835e73b3fd3c276f9a7de56 +0 -0
  127. data/spec/dummy/tmp/cache/assets/test/sprockets/203047a4141cf08162077e7290421bbb +0 -0
  128. data/spec/dummy/tmp/cache/assets/test/sprockets/210050da208fb75a75b701bfa4e8470f +0 -0
  129. data/spec/dummy/tmp/cache/assets/test/sprockets/2733033a48e2e8a6fdd6e4ab371689ee +0 -0
  130. data/spec/dummy/tmp/cache/assets/test/sprockets/27a59e08207d3ae723f2b2b3d22264c3 +0 -0
  131. data/spec/dummy/tmp/cache/assets/test/sprockets/29a6c93aeb91d07a5533b3adba7697d0 +0 -0
  132. data/spec/dummy/tmp/cache/assets/test/sprockets/29ac2be474969468157245a7f8737fa4 +0 -0
  133. data/spec/dummy/tmp/cache/assets/test/sprockets/314e44d29ecc7449c9ca3b754329c245 +0 -0
  134. data/spec/dummy/tmp/cache/assets/test/sprockets/3162d1398d6940ee5e933b7ab4d2273d +0 -0
  135. data/spec/dummy/tmp/cache/assets/test/sprockets/3d80e2a5faaf2bd987d804c17aead745 +0 -0
  136. data/spec/dummy/tmp/cache/assets/test/sprockets/4155a156cc6ffb880b56dbc34346fbc0 +0 -0
  137. data/spec/dummy/tmp/cache/assets/test/sprockets/4166fe47cddf4ba18c53b843d69b4c5f +0 -0
  138. data/spec/dummy/tmp/cache/assets/test/sprockets/441f922b26aae90d40e319b6ed0a3bc8 +0 -0
  139. data/spec/dummy/tmp/cache/assets/test/sprockets/4432a4b71ce9551df8d4b3860f4b9be3 +0 -0
  140. data/spec/dummy/tmp/cache/assets/test/sprockets/448c1e44da04c20f27eaa31ad8c4391e +0 -0
  141. data/spec/dummy/tmp/cache/assets/test/sprockets/47df4fdca8a2ff6a43c18d7aa1ffd9c5 +0 -0
  142. data/spec/dummy/tmp/cache/assets/test/sprockets/5479c7b4fba9179d4d1dc59ae64964c1 +0 -0
  143. data/spec/dummy/tmp/cache/assets/test/sprockets/566fbede2036fd06a20e7e52e30ddc7d +0 -0
  144. data/spec/dummy/tmp/cache/assets/test/sprockets/5a3ab9afd151c265cf11c621c0ddbe00 +0 -0
  145. data/spec/dummy/tmp/cache/assets/test/sprockets/647038c58f26163a9217646e1e5f09ae +0 -0
  146. data/spec/dummy/tmp/cache/assets/test/sprockets/64e0150fafbd9aaa3822efa1abb0211d +0 -0
  147. data/spec/dummy/tmp/cache/assets/test/sprockets/65c2d908dbca1bc833ae0b552f6c27fa +0 -0
  148. data/spec/dummy/tmp/cache/assets/test/sprockets/6882b260ae69f1594eff540d803e5ac3 +0 -0
  149. data/spec/dummy/tmp/cache/assets/test/sprockets/69f4b83d363269ee4122bbbaaa1325d1 +0 -0
  150. data/spec/dummy/tmp/cache/assets/test/sprockets/6e1aee34907ba1ccdbfca2deb18a5adb +0 -0
  151. data/spec/dummy/tmp/cache/assets/test/sprockets/726d3f6850549a59954f0bf5584cfd9f +0 -0
  152. data/spec/dummy/tmp/cache/assets/test/sprockets/7ab9ac7cc2529219a4c68fd7854d5c0f +0 -0
  153. data/spec/dummy/tmp/cache/assets/test/sprockets/7bac3aca131c870ad5b07d8ccdc86e06 +0 -0
  154. data/spec/dummy/tmp/cache/assets/test/sprockets/80392355fc6e00cf69b2cd46978ebcbe +0 -0
  155. data/spec/dummy/tmp/cache/assets/test/sprockets/874b42463fefbb0bce47775722ebb4ae +0 -0
  156. data/spec/dummy/tmp/cache/assets/test/sprockets/87ef8ddd2847d19f22a16d726b88b433 +0 -0
  157. data/spec/dummy/tmp/cache/assets/test/sprockets/91ae0bd43f98b4f1f111e0bdc178302c +0 -0
  158. data/spec/dummy/tmp/cache/assets/test/sprockets/97d99b466bedb69706f4815fa26b69f3 +0 -0
  159. data/spec/dummy/tmp/cache/assets/test/sprockets/990cf740a7968767a327f6186313badb +0 -0
  160. data/spec/dummy/tmp/cache/assets/test/sprockets/9a690664139b0393787d6454b4697183 +0 -0
  161. data/spec/dummy/tmp/cache/assets/test/sprockets/a2b14d5c46db32da9183354fb3fcd0bf +0 -0
  162. data/spec/dummy/tmp/cache/assets/test/sprockets/a6d6196cfd275dea0718553a95f997a5 +0 -0
  163. data/spec/dummy/tmp/cache/assets/test/sprockets/bc83340720a76d8208112f6aecbff08e +0 -0
  164. data/spec/dummy/tmp/cache/assets/test/sprockets/c29c465933e7e86d6f6224f5e78c5baf +0 -0
  165. data/spec/dummy/tmp/cache/assets/test/sprockets/c975834a35907ecd2d314bbd6844ee88 +0 -0
  166. data/spec/dummy/tmp/cache/assets/test/sprockets/cad26971ff72bbf5aa5f4e6a469389eb +0 -0
  167. data/spec/dummy/tmp/cache/assets/test/sprockets/cf4affc50c6303e717d6514380d1f417 +0 -0
  168. data/spec/dummy/tmp/cache/assets/test/sprockets/d0d5517256edcd1f83b11a13c6ca040f +0 -0
  169. data/spec/dummy/tmp/cache/assets/test/sprockets/d3087f7df919b7f0d8142ee163db6c79 +0 -0
  170. data/spec/dummy/tmp/cache/assets/test/sprockets/d8b357d5741711da2dd710af0e15d268 +0 -0
  171. data/spec/dummy/tmp/cache/assets/test/sprockets/decc27a1fa9ea32fe0dcc18619e32587 +0 -0
  172. data/spec/dummy/tmp/cache/assets/test/sprockets/df796f7ea525be06a82ebc3a4f97ffec +0 -0
  173. data/spec/dummy/tmp/cache/assets/test/sprockets/e22a1b318499e7f06ea9274e18ab004f +0 -0
  174. data/spec/dummy/tmp/cache/assets/test/sprockets/e298cc3a8203ab1fb6978a90254f8926 +0 -0
  175. data/spec/dummy/tmp/cache/assets/test/sprockets/e378dcebf3c529db0d6e57d0c8dc3904 +0 -0
  176. data/spec/dummy/tmp/cache/assets/test/sprockets/e868f358fe87542c483951a2d9f532b6 +0 -0
  177. data/spec/dummy/tmp/cache/assets/test/sprockets/e8a6adf580ebd1942f4aabe6738678c2 +0 -0
  178. data/spec/dummy/tmp/cache/assets/test/sprockets/ed71f99e0a9653296abd5d6f501dd898 +0 -0
  179. data/spec/dummy/tmp/cache/assets/test/sprockets/efe6f2d16d3a1819817efb8734daf8af +0 -0
  180. data/spec/dummy/tmp/cache/assets/test/sprockets/f03e99bdb4159ae3485ca2858716e19b +0 -0
  181. data/spec/dummy/tmp/cache/assets/test/sprockets/f4648036e284d9aa3468f4f7b9bbb967 +0 -0
  182. data/spec/dummy/tmp/cache/assets/test/sprockets/f961f7434a28c0fe24bf4ffe6cba75fb +0 -0
  183. data/spec/dummy/tmp/cache/assets/test/sprockets/f9bbf698d099ead559e5ba4a5a4c5538 +0 -0
  184. data/spec/dummy/tmp/cache/assets/test/sprockets/fb8d3825617fcc11dfa614a51effee0e +0 -0
  185. data/spec/helpers/prosperity/dashboard_graphs_helper_spec.rb +6 -0
  186. data/spec/helpers/prosperity/dashboards_helper_spec.rb +6 -0
  187. data/spec/helpers/prosperity/{metrics_helper_spec.rb → graph_helper_spec.rb} +1 -1
  188. data/spec/lib/prosperity/aggregate/aggregate_builder_spec.rb +65 -0
  189. data/spec/lib/prosperity/aggregate/average_spec.rb +7 -0
  190. data/spec/lib/prosperity/aggregate/base_spec.rb +7 -0
  191. data/spec/lib/prosperity/aggregate/count_spec.rb +7 -0
  192. data/spec/lib/prosperity/aggregate/maximum_spec.rb +7 -0
  193. data/spec/lib/prosperity/aggregate/minimum_spec.rb +7 -0
  194. data/spec/lib/prosperity/aggregate/sql_spec.rb +7 -0
  195. data/spec/lib/prosperity/aggregate/sum_spec.rb +7 -0
  196. data/spec/lib/prosperity/aggregate/with_column_spec.rb +7 -0
  197. data/spec/lib/prosperity/aggregate_spec.rb +7 -0
  198. data/spec/lib/prosperity/extractors/base_spec.rb +27 -2
  199. data/spec/lib/prosperity/extractors/change_spec.rb +72 -0
  200. data/spec/lib/prosperity/extractors/interval_spec.rb +153 -0
  201. data/spec/lib/prosperity/extractors/total_spec.rb +79 -0
  202. data/spec/lib/prosperity/helpers/time_spec.rb +7 -0
  203. data/spec/lib/prosperity/metric_finder_spec.rb +7 -0
  204. data/spec/lib/prosperity/metric_spec.rb +126 -1
  205. data/spec/lib/prosperity/periods_spec.rb +14 -0
  206. data/spec/models/prosperity/dashboard_graph_spec.rb +6 -0
  207. data/spec/models/prosperity/dashboard_spec.rb +6 -0
  208. data/spec/models/prosperity/graph_line_spec.rb +6 -0
  209. data/spec/models/prosperity/graph_spec.rb +6 -0
  210. data/spec/spec_helper.rb +5 -2
  211. data/spec/support/shared/extractors.rb +64 -0
  212. data/spec/support/test_database.rb +1 -0
  213. data/vendor/assets/javascripts/highcharts.js +285 -0
  214. metadata +317 -62
  215. data/app/helpers/prosperity/metrics_helper.rb +0 -11
  216. data/app/views/layouts/prosperity/application.html.haml +0 -37
  217. data/app/views/prosperity/metrics/index.html.haml +0 -7
  218. data/db/migrate/20131026214127_test.rb +0 -4
  219. data/lib/prosperity/extractors/count.rb +0 -18
  220. data/lib/prosperity/extractors/group.rb +0 -23
  221. data/spec/lib/prosperity/extractors/count_spec.rb +0 -35
  222. data/spec/lib/prosperity/extractors/group_spec.rb +0 -34
  223. data/vendor/assets/javascripts/chart.js +0 -1426
@@ -0,0 +1,8 @@
1
+ <h1>Metrics</h1>
2
+ <ul>
3
+ <% @metrics.each do |metric| %>
4
+ <li>
5
+ <%= link_to_metric metric %>
6
+ </li>
7
+ <% end %>
8
+ </ul>
@@ -0,0 +1,23 @@
1
+ <h1>
2
+ <%= @metric.title %>
3
+ </h1>
4
+
5
+ <div class="metric" data-url="<%= metric_path(params) %>"></div>
6
+
7
+ <%= form_tag(metric_path(params[:id]), method: 'get') do %>
8
+ <h2>Settings</h2>
9
+ <div class='well'>
10
+ <h3>Option</h3>
11
+
12
+ <%= select_tag :option, options_for_select(@metric.options.keys, option) %>
13
+
14
+ <h3>Period</h3>
15
+ <% Prosperity::Periods::ALL.each do |key, period_object| %>
16
+ <%= radio_button_tag "period", key, key == period %>
17
+ <%= label_tag "period_#{key}", key.titleize %>
18
+ <% end %>
19
+
20
+ <%= render 'prosperity/shared/date_range' %>
21
+ <%= submit_tag "Update", class: 'btn btn-primary' %>
22
+ </div>
23
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <div class="form-group">
2
+ <%= label_tag :start_time %>
3
+ <%= date_field_tag :start_time, start_time.to_date %>
4
+ </div>
5
+
6
+ <div class="form-group">
7
+ <%= label_tag :end_time %>
8
+ <%= date_field_tag :end_time, end_time.try(:to_date) %>
9
+ </div>
data/config/routes.rb CHANGED
@@ -1,5 +1,15 @@
1
- Prosperity::Engine.routes.draw do
2
- resources :metrics
1
+ Prosperity::Engine.routes.draw do
2
+ resources :dashboards do
3
+ post "graphs/:graph_id", to: "dashboard_graphs#create", as: :dashboard_graphs
4
+ delete "graphs/:graph_id", to: "dashboard_graphs#destroy"
5
+ end
6
+ resources :metrics do
7
+ member do
8
+ get :data
9
+ end
10
+ end
11
+
12
+ resources :graphs
3
13
 
4
14
  root to: 'metrics#index'
5
15
  end
@@ -0,0 +1,34 @@
1
+ class Dashboards < ActiveRecord::Migration
2
+ def change
3
+ # Note: foreign_key: false is for schema_plus support
4
+ create_table :prosperity_dashboards do |t|
5
+ t.string :title, null: false
6
+ t.boolean :default, null: false
7
+ t.timestamps
8
+ end
9
+
10
+ create_table :prosperity_graph_lines do |t|
11
+ t.integer :graph_id, null: false, foreign_key: false
12
+ t.string :option, null: false
13
+ t.string :metric, null: false
14
+ t.string :extractor, null: false
15
+ t.timestamps
16
+ end
17
+
18
+ create_table :prosperity_graphs do |t|
19
+ t.string :title, null: false
20
+ t.string :period, null: false
21
+ t.timestamps
22
+ end
23
+
24
+ create_table :prosperity_dashboard_graphs do |t|
25
+ t.integer :graph_id, null: false, foreign_key: false
26
+ t.integer :dashboard_id, null: false, foreign_key: false
27
+ t.timestamps
28
+ end
29
+
30
+ add_index :prosperity_dashboard_graphs, [:graph_id, :dashboard_id], unique: true
31
+ add_index :prosperity_dashboard_graphs, :dashboard_id
32
+ add_index :prosperity_graph_lines, :graph_id
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generates a new Prosperity metric for your application.
3
+
4
+ Example:
5
+ rails generate metric User
6
+
7
+ This will create:
8
+ app/properity/user_metric.rb
@@ -0,0 +1,7 @@
1
+ class MetricGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path('../templates', __FILE__)
3
+
4
+ def generate_metric
5
+ template "metric.rb.erb", File.join("app", "prosperity", class_path, "#{file_name}_metric.rb")
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ class <%= class_name%>Metric < Prosperity::Metric
2
+ scope { <%= class_name %> }
3
+ end
@@ -0,0 +1,42 @@
1
+ module Prosperity
2
+ class Aggregate::AggregateBuilder
3
+ attr_reader :block
4
+
5
+ def initialize(string = nil, &block)
6
+ raise "Can't specify a string and a block" if string && block_given?
7
+
8
+ @string = string
9
+ @block = block
10
+ end
11
+
12
+ def build
13
+ res = @string ? @string : instance_eval(&block)
14
+ if res.is_a?(String)
15
+ Aggregate::Sql.new(res)
16
+ else
17
+ res
18
+ end
19
+ end
20
+
21
+ def count
22
+ Aggregate::Count.new
23
+ end
24
+
25
+ def sum(column)
26
+ Aggregate::Sum.new(column)
27
+ end
28
+
29
+ def maximum(column)
30
+ Aggregate::Maximum.new(column)
31
+ end
32
+
33
+ def minimum(column)
34
+ Aggregate::Minimum.new(column)
35
+ end
36
+
37
+ def average(column)
38
+ Aggregate::Average.new(column)
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,12 @@
1
+ module Prosperity
2
+ class Aggregate::Average < Aggregate::WithColumn
3
+ def to_sql
4
+ "AVG(#{column})"
5
+ end
6
+
7
+ def apply(scope)
8
+ scope.average(column)
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,5 @@
1
+ module Prosperity
2
+ class Aggregate::Base
3
+ end
4
+ end
5
+
@@ -0,0 +1,12 @@
1
+ module Prosperity
2
+ class Aggregate::Count < Aggregate::Base
3
+ def to_sql
4
+ "COUNT(1)"
5
+ end
6
+
7
+ def apply(scope)
8
+ scope.count
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,12 @@
1
+ module Prosperity
2
+ class Aggregate::Maximum < Aggregate::WithColumn
3
+ def to_sql
4
+ "MAX(#{column})"
5
+ end
6
+
7
+ def apply(scope)
8
+ scope.maximum(column)
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,12 @@
1
+ module Prosperity
2
+ class Aggregate::Minimum < Aggregate::WithColumn
3
+ def to_sql
4
+ "MIN(#{column})"
5
+ end
6
+
7
+ def apply(scope)
8
+ scope.minimum(column)
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,12 @@
1
+ module Prosperity
2
+ class Aggregate::Sql < Aggregate::Base
3
+ def initialize(sql)
4
+ @sql = sql
5
+ end
6
+
7
+ def to_sql
8
+ @sql
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,12 @@
1
+ module Prosperity
2
+ class Aggregate::Sum < Aggregate::WithColumn
3
+ def to_sql
4
+ "SUM(#{column})"
5
+ end
6
+
7
+ def apply(scope)
8
+ scope.sum(column)
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,10 @@
1
+ module Prosperity
2
+ class Aggregate::WithColumn
3
+ attr_reader :column
4
+
5
+ def initialize(column)
6
+ @column = column
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,5 @@
1
+ module Prosperity
2
+ class Aggregate
3
+ end
4
+ end
5
+
@@ -5,5 +5,19 @@ module Prosperity
5
5
  config.after_initialize do
6
6
  require "prosperity/exception"
7
7
  end
8
+
9
+ config.generators do |g|
10
+ g.test_framework :rspec, :fixtures => false
11
+ g.view_specs false
12
+ g.fixture_replacement nil
13
+ end
14
+
15
+ initializer :append_migrations do |app|
16
+ unless app.root.to_s.match root.to_s
17
+ config.paths["db/migrate"].expanded.each do |expanded_path|
18
+ app.config.paths["db/migrate"] << expanded_path
19
+ end
20
+ end
21
+ end
8
22
  end
9
23
  end
@@ -4,7 +4,25 @@ module Prosperity
4
4
 
5
5
  class MissingScope < Exception
6
6
  def initialize
7
- super "You must specify a scope for all metrics."
7
+ super "You have asked for the scope of a metric with no scope."
8
+ end
9
+ end
10
+
11
+ class MissingSql < Exception
12
+ def initialize
13
+ super "You have asked for the sql of a metric with no sql."
14
+ end
15
+ end
16
+
17
+ class MissingValueAt < Exception
18
+ def initialize
19
+ super "You have asked for the value_at, but none was specified"
20
+ end
21
+ end
22
+
23
+ class SqlMetricCannotHaveOption < Exception
24
+ def initialize
25
+ super "Sql metrics cannot have options"
8
26
  end
9
27
  end
10
28
  end
@@ -1,11 +1,39 @@
1
1
  module Prosperity
2
2
  class Extractors::Base
3
- attr_reader :metric, :start_time, :end_time, :period
3
+ attr_reader :metric, :start_time, :end_time, :period, :option
4
4
 
5
- def initialize(metric, start_time, end_time, period)
6
- @metric, @start_time, @end_time, @period =
7
- metric, start_time, end_time, period
5
+ def initialize(metric, option, start_time, end_time, period)
6
+ @metric, @option, @start_time, @end_time, @period =
7
+ metric, option, period.floor_date.call(start_time), period.ceil_date.call(end_time), period
8
+ end
9
+
10
+ def scope
11
+ @metric.options.fetch(option).block.call(metric.scope)
12
+ end
13
+
14
+ def key
15
+ self.class.key
16
+ end
17
+
18
+ def label
19
+ "#{metric.title} by #{key} with option #{option}"
20
+ end
21
+
22
+ def aggregate
23
+ metric.aggregate
24
+ end
25
+
26
+ private
27
+ def count_up_to_date_with_sql(date)
28
+ fragment = <<-SQL
29
+ WITH prosperity_metric_count AS (
30
+ #{metric.sql}
31
+ )
32
+ SELECT #{aggregate.to_sql} AS RESULT FROM prosperity_metric_count
33
+ WHERE #{metric.group_by} < '#{date.iso8601}'
34
+ SQL
35
+ result = ActiveRecord::Base.connection.execute(fragment)
36
+ result.to_a.first["result"].to_f
8
37
  end
9
38
  end
10
39
  end
11
-
@@ -0,0 +1,35 @@
1
+ module Prosperity
2
+ class Extractors::Change < Extractors::Base
3
+ def self.key
4
+ 'change'
5
+ end
6
+
7
+ def to_a
8
+ data = []
9
+
10
+ period.each_period(start_time, end_time) do |start_time|
11
+ if metric.sql?
12
+ new = count_up_to_date_with_sql(start_time)
13
+ last = count_up_to_date_with_sql(start_time - period.duration)
14
+ elsif metric.ruby?
15
+ new = metric.value_at.call(start_time, period)
16
+ last = metric.value_at.call(start_time - period.duration, period)
17
+ else
18
+ new = aggregate.apply(scope.where("#{metric.group_by} < ?", start_time))
19
+ last = aggregate.apply(scope.where("#{metric.group_by} < ?", start_time - period.duration))
20
+ end
21
+
22
+ change = if last && last > 0
23
+ ((new.to_f / last) - 1.0) * 100
24
+ else
25
+ 0.0
26
+ end
27
+
28
+ data << change
29
+ end
30
+
31
+ data
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,51 @@
1
+ module Prosperity
2
+ class Extractors::Interval < Extractors::Base
3
+ def self.key
4
+ "interval"
5
+ end
6
+
7
+ def to_a
8
+ if metric.sql?
9
+ fragment = <<-SQL
10
+ WITH prosperity_metric_count AS (
11
+ #{metric.sql}
12
+ )
13
+ SELECT to_char(#{metric.group_by}, '#{period.db_strf_str}') AS bucket, #{aggregate.to_sql} AS result
14
+ FROM prosperity_metric_count
15
+ WHERE (#{metric.group_by} BETWEEN '#{@start_time.iso8601}' AND '#{@end_time.iso8601}')
16
+ GROUP BY bucket
17
+ SQL
18
+ result = ActiveRecord::Base.connection.execute(fragment)
19
+ s = result.to_a.inject({}) {|accum, el|
20
+ accum.update(el["bucket"] => el["result"].to_f)
21
+ }
22
+ elsif metric.ruby?
23
+ data = []
24
+ period.each_period(start_time, end_time) do |start_time|
25
+ new = metric.value_at.call(start_time, period)
26
+ last = metric.value_at.call(start_time - period.duration, period)
27
+
28
+ data << new - last
29
+ end
30
+
31
+ return data
32
+ else
33
+ s = scope.where("#{metric.group_by} BETWEEN ? AND ?", @start_time, @end_time)
34
+ s = s.group("to_char(#{metric.group_by}, '#{period.db_strf_str}')")
35
+ s = aggregate.apply(s)
36
+ end
37
+
38
+
39
+ data = []
40
+
41
+ period.each_period(start_time, end_time) do |start_time|
42
+ str = start_time.strftime(period.ruby_strf_str)
43
+ value = s.has_key?(str) ? s[str].to_f : 0.0
44
+ data << value
45
+ end
46
+
47
+ data
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,23 @@
1
+ module Prosperity
2
+ class Extractors::Total < Extractors::Base
3
+ def self.key
4
+ "total"
5
+ end
6
+
7
+ def to_a
8
+ data = []
9
+
10
+ period.each_period(start_time, end_time) do |start_time|
11
+ if metric.sql?
12
+ data << count_up_to_date_with_sql(start_time)
13
+ elsif metric.ruby?
14
+ data << metric.value_at.call(start_time, period)
15
+ else
16
+ data << metric.aggregate.apply(scope.where("#{metric.group_by} < ?", start_time))
17
+ end
18
+ end
19
+
20
+ data
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module Prosperity
2
+ class Helpers::Time
3
+ def self.beginning_of_hour(date)
4
+ DateTime.new(date.year, date.month, date.day, date.hour)
5
+ end
6
+
7
+ def self.end_of_hour(date)
8
+ DateTime.new(date.year, date.month, date.day, date.hour) + 1.hour
9
+ end
10
+ end
11
+ end
12
+
@@ -9,32 +9,117 @@ module Prosperity
9
9
  end
10
10
  end
11
11
 
12
- def self.options(name = nil, &block)
12
+ def self.sql(fragment = nil, &block)
13
+ if fragment && block_given?
14
+ raise ArgumentError, "Must pass string or block but not both"
15
+ elsif fragment
16
+ @sql = fragment
17
+ elsif block_given?
18
+ @sql = block.call
19
+ elsif @sql.nil?
20
+ raise MissingSql.new
21
+ else
22
+ @sql
23
+ end
24
+ end
25
+
26
+ def self.option(name, &block)
27
+ raise SqlMetricCannotHaveOption.new unless @sql.nil?
13
28
  @options ||= default_options
14
29
  if block_given?
15
30
  @options[name] = Metrics::Option.new(name, &block)
16
31
  else
17
- raise MissingScope.new if @options.nil?
18
- @options
32
+ raise MissingScope.new
33
+ end
34
+ end
35
+
36
+ def self.value_at(&block)
37
+ if block_given?
38
+ @value_at = block
39
+ else
40
+ @value_at or raise MissingValueAt
41
+ end
42
+ end
43
+
44
+ def self.options
45
+ @options ||= default_options
46
+ end
47
+
48
+ def self.group_by(column = nil)
49
+ if column
50
+ @group_by = column
51
+ else
52
+ @group_by || :created_at
53
+ end
54
+ end
55
+
56
+ def self.aggregate(&block)
57
+ if block_given?
58
+ @aggregate = ::Prosperity::Aggregate::AggregateBuilder.new(&block).build
59
+ else
60
+ @aggregate || ::Prosperity::Aggregate::Count.new
19
61
  end
20
62
  end
21
63
 
64
+ def self.extractors
65
+ [Extractors::Interval, Extractors::Total, Extractors::Change].inject({}) do |h, ext|
66
+ h[ext.key] = ext
67
+ h
68
+ end
69
+ end
70
+
71
+ def self.sql?
72
+ @sql.present?
73
+ end
74
+
75
+ def self.ruby?
76
+ @value_at.present?
77
+ end
78
+
22
79
  def extractors
23
- [Extractors::Group, Extractors::Count]
80
+ self.class.extractors.values
24
81
  end
25
82
 
26
83
  def group_by
27
- :created_at
84
+ self.class.group_by
28
85
  end
29
86
 
30
87
  def scope
31
88
  self.class.scope
32
89
  end
33
90
 
91
+ def sql
92
+ self.class.sql
93
+ end
94
+
95
+ def value_at
96
+ self.class.value_at
97
+ end
98
+
34
99
  def options
35
100
  self.class.options
36
101
  end
37
102
 
103
+ def aggregate
104
+ self.class.aggregate
105
+ end
106
+
107
+ def title
108
+ self.class.to_s.gsub(/Metric$/, "").titleize
109
+ end
110
+
111
+ def id
112
+ self.class.name
113
+ end
114
+
115
+ def sql?
116
+ self.class.sql?
117
+ end
118
+
119
+ def ruby?
120
+ self.class.ruby?
121
+ end
122
+
38
123
  private
39
124
 
40
125
  def self.default_options
@@ -9,6 +9,16 @@ module Prosperity
9
9
  def self.all
10
10
  self.new(File.join(Rails.root, "app/prosperity")).metrics
11
11
  end
12
+
13
+ def self.find_by_name(name)
14
+ klass = name.constantize
15
+
16
+ if klass < Metric
17
+ klass
18
+ else
19
+ nil
20
+ end
21
+ end
12
22
  end
13
23
  end
14
24
 
@@ -1,5 +1,7 @@
1
1
  module Prosperity
2
2
  class Metrics::Option
3
+ attr_reader :name, :block
4
+
3
5
  def initialize(name, &block)
4
6
  @name = name
5
7
  @block = block
@@ -1,11 +1,12 @@
1
1
  module Prosperity
2
- class Period < Struct.new(:db_strf_str, :ruby_strf_str, :duration)
2
+ class Period < Struct.new(:db_strf_str, :ruby_strf_str, :duration, :floor_date, :ceil_date)
3
3
  def each_period(start_time, end_time)
4
- while start_time < end_time
4
+ start_time = floor_date.call(start_time)
5
+ end_time = ceil_date.call(end_time) + 1
6
+ while start_time <= end_time
5
7
  yield start_time
6
8
  start_time += duration
7
9
  end
8
10
  end
9
11
  end
10
12
  end
11
-
@@ -1,6 +1,17 @@
1
1
  module Prosperity
2
2
  class Periods
3
- MONTH = Period.new("YYYY-MM", "%Y-%m", 1.month)
3
+ MONTH = Period.new("YYYY-MM", "%Y-%m", 1.month, ->(start_ts){ start_ts.beginning_of_month }, ->(end_ts){ end_ts.end_of_month })
4
+ WEEK = Period.new("YYYY-WW", "%Y-%W", 1.week, ->(start_ts){ start_ts.beginning_of_week }, ->(end_ts){ end_ts.end_of_week })
5
+ DAY = Period.new("YYYY-MM-DD", "%Y-%m-%d", 1.day, ->(start_ts){ start_ts.at_midnight }, ->(end_ts){ end_ts.at_end_of_day })
6
+ HOUR = Period.new("YYYY-MM-DD-H", "%Y-%m-%d-%h", 1.hour,
7
+ ->(start_ts){ Helpers::Time.beginning_of_hour(start_ts) },
8
+ ->(end_ts){ Helpers::Time.end_of_hour(end_ts) })
9
+
10
+ ALL = {
11
+ month: MONTH,
12
+ week: WEEK,
13
+ day: DAY,
14
+ hour: HOUR,
15
+ }.with_indifferent_access.freeze
4
16
  end
5
17
  end
6
-
@@ -1,3 +1,3 @@
1
1
  module Prosperity
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end