quick_search-core 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -6
  3. data/app/assets/javascripts/quick_search.js.erb +9 -0
  4. data/app/assets/javascripts/quick_search/appstats.js.erb +73 -0
  5. data/app/assets/javascripts/quick_search/appstats_best_bets.js.erb +905 -0
  6. data/app/assets/javascripts/quick_search/appstats_clicks_overview.js.erb +719 -0
  7. data/app/assets/javascripts/quick_search/appstats_general_statistics.js.erb +908 -0
  8. data/app/assets/javascripts/quick_search/appstats_sessions_details.js.erb +660 -0
  9. data/app/assets/javascripts/quick_search/appstats_sessions_overview.js.erb +481 -0
  10. data/app/assets/javascripts/quick_search/appstats_spelling_suggestions.js.erb +902 -0
  11. data/app/assets/javascripts/quick_search/appstats_top_searches.js.erb +633 -0
  12. data/app/assets/javascripts/quick_search/event_tracking.js.erb +22 -33
  13. data/app/assets/javascripts/quick_search/xhr_search.js +7 -3
  14. data/app/assets/stylesheets/quick_search/appstats.scss +148 -0
  15. data/app/assets/stylesheets/quick_search/quick_search.scss +0 -16
  16. data/app/controllers/concerns/quick_search/doi_trap.rb +1 -1
  17. data/app/controllers/quick_search/appstats_controller.rb +267 -144
  18. data/app/controllers/quick_search/logging_controller.rb +165 -0
  19. data/app/controllers/quick_search/search_controller.rb +1 -23
  20. data/app/controllers/quick_search/typeahead_controller.rb +48 -0
  21. data/app/models/event.rb +6 -0
  22. data/app/models/search.rb +7 -0
  23. data/app/models/session.rb +9 -0
  24. data/app/searchers/quick_search/best_bets_searcher.rb +1 -1
  25. data/app/views/layouts/quick_search/_google_analytics.html.erb +4 -9
  26. data/app/views/layouts/quick_search/_search_form.html.erb +2 -2
  27. data/app/views/layouts/quick_search/application.html.erb +4 -0
  28. data/app/views/quick_search/appstats/_date_range.html.erb +14 -13
  29. data/app/views/quick_search/appstats/_graph_best_bets.html.erb +6 -0
  30. data/app/views/quick_search/appstats/_graph_clicks_overview.html.erb +12 -0
  31. data/app/views/quick_search/appstats/_graph_general_statistics.html.erb +19 -0
  32. data/app/views/quick_search/appstats/_graph_sessions_details.html.erb +19 -0
  33. data/app/views/quick_search/appstats/_graph_sessions_overview.html.erb +9 -0
  34. data/app/views/quick_search/appstats/_graph_spelling_suggestions.html.erb +6 -0
  35. data/app/views/quick_search/appstats/_graph_top_searches.html.erb +6 -0
  36. data/app/views/quick_search/appstats/_menu.html.erb +16 -3
  37. data/app/views/quick_search/appstats/_sessions_details_device_filters.html.erb +9 -0
  38. data/app/views/quick_search/appstats/_sessions_details_location_filters.html.erb +9 -0
  39. data/app/views/quick_search/appstats/_sessions_overview_graph_filters.html.erb +9 -0
  40. data/app/views/quick_search/appstats/clicks_overview.html.erb +3 -14
  41. data/app/views/quick_search/appstats/index.html.erb +3 -23
  42. data/app/views/quick_search/appstats/sessions_details.html.erb +9 -0
  43. data/app/views/quick_search/appstats/sessions_overview.html.erb +9 -0
  44. data/app/views/quick_search/appstats/top_searches.html.erb +3 -32
  45. data/app/views/quick_search/appstats/top_spot.html.erb +7 -7
  46. data/app/views/quick_search/search/_module_with_paging.html.erb +1 -1
  47. data/app/views/quick_search/search/_result_title.html.erb +1 -1
  48. data/config/quicksearch_config.yml.example +3 -0
  49. data/config/routes.rb +31 -3
  50. data/db/migrate/20161201141003_create_sessions.rb +12 -0
  51. data/db/migrate/20161201142811_change_event_columns.rb +11 -0
  52. data/db/migrate/20161201143901_add_session_id_to_searches.rb +5 -0
  53. data/db/migrate/20161212192454_add_date_string_columns.rb +11 -0
  54. data/db/seeds.rb +83 -7
  55. data/lib/generators/quick_search/templates/quick_search_typeahead.js +253 -0
  56. data/lib/generators/quick_search/typeahead_generator.rb +162 -0
  57. data/lib/quick_search/engine.rb +1 -0
  58. data/lib/quick_search/version.rb +1 -1
  59. data/test/dummy/db/schema.rb +25 -6
  60. data/test/dummy/log/benchmark.log +121 -241
  61. data/test/dummy/log/development.log +236 -0
  62. data/test/dummy/tmp/cache/assets/sprockets/v3.0/0b/0bXp2nnjPLNuYQRAMo1RkhNr8RSC8vqZo_7soo0XZTE.cache +1 -0
  63. data/test/dummy/tmp/cache/assets/sprockets/v3.0/3p/3pAfj3gt9c9rLmOrDkMvW2r6Swm5YhuGQ1EN3va9LDE.cache +6 -0
  64. data/test/dummy/tmp/cache/assets/sprockets/v3.0/3r/3r7qg-LCIcD5882b6GKgRTZycpAlhoSNJccVH06yRCQ.cache +3 -0
  65. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{UA/UAOIipK_ejTAUFI6XxASoweyUiC3t64VfVzV-MfsFD8.cache → 4h/4hLiHzJdffGLMZCeIkbisOdFQSyqs54YkoZpBpGotVk.cache} +0 -0
  66. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{Hr/Hr-kCuPY72nIKAdnsDTz9JqXd_z1WL6FSGMAq4HppZg.cache → 5Y/5Yh8GnSzywqhuMWMloKnL9FnuIxa9rBw6hN2Ecv0urw.cache} +0 -0
  67. data/test/dummy/tmp/cache/assets/sprockets/v3.0/8h/8hHsBZ0Il3GVKNj0HgTh-s-XpPVkHrcSY0EJ4v548sk.cache +4 -0
  68. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{fI/fInu_Gja7ik39t207tqj4CtlL0akQRbjD2T7qEtd0zE.cache → 8n/8nKAboU_CeVIX30-Q9BtSV5TXdE07WU8hO0OQ0N3KOs.cache} +0 -0
  69. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{dz/dzCaa80YgyvJINTgsyrVhKv9UQ1OC82GfAsA-IP96iQ.cache → Af/Afug8duevWkZFPkOMnZ5z_IbRT6JMZo4jw1QFmLiXXA.cache} +0 -0
  70. data/test/dummy/tmp/cache/assets/sprockets/v3.0/C2/C24SLDoUbdiHRmPZx5lIZ3XPXmn9FDq7j7gZmbpDtcE.cache +1 -0
  71. data/test/dummy/tmp/cache/assets/sprockets/v3.0/CH/CHuDjj47dHGqRD3uN1JNwnlFyjjEKB09KR6WVsLj2Do.cache +3 -0
  72. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Ci/Cif3NOwhu5i6Qm3iRv2gNJLi_cu8yDVz0Nrq8TZu1tc.cache +3 -0
  73. data/test/dummy/tmp/cache/assets/sprockets/v3.0/FQ/FQWd8V-Pek1LBAfW9vzcG-FpWySsPRhMVzH9DlPxLWU.cache +1 -0
  74. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Fa/FaYEz8CfLTkxVZvnbgKGvJMhhBwNvEgsZgUwaHNRlaQ.cache +0 -0
  75. data/test/dummy/tmp/cache/assets/sprockets/v3.0/IA/IALQq6ICbs3VBSRsaUmGuN73Qv5wDfWTP86VmMh_sVs.cache +0 -0
  76. data/test/dummy/tmp/cache/assets/sprockets/v3.0/K1/K1jnTrW-_s64kqCA6fnoQ-ID-rDaGX34J-TNPyzDbLs.cache +1 -0
  77. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Mz/MzXE1Go_GKyu6AvJPhw394BtPM_jVTf0hMwobBi15tk.cache +1 -0
  78. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Os/OsIhoBDw5N2AWGQ-EMK79tgwPS1AC6Dy_gMSJdOv2Ss.cache +0 -0
  79. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{B5/B5ptGSZGmgHvoK2UDCAtpMhWxJEKxKFhyL9ACeBfzQk.cache → PC/PCth9nzoRrp5GI3Ut8FxlGpXjH63V7k2p4PRwfyu2sQ.cache} +0 -0
  80. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Pw/Pw3Jv2p7MsQz6pttCZig6VMVQ30sRyIDRS-2nM32yqY.cache +3 -0
  81. data/test/dummy/tmp/cache/assets/sprockets/v3.0/RI/RIOgtcuvLAJZl62RvAzqine3jCO3i2qyhswiW2G1bw8.cache +3 -0
  82. data/test/dummy/tmp/cache/assets/sprockets/v3.0/R_/R_6WhfFaPUAOAFfFd4bgUonI5aB4Fa6iOMY2J6G3U98.cache +4 -0
  83. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{pj/pjJDYrBipDo6Tr9_-W4UDzfZZ4UPTy9YpXrBQFRXnlo.cache → S3/S3gPdA-Zqya5r6wNhnlwqGV3B3-GDaA8i1fL_jo66AI.cache} +0 -0
  84. data/test/dummy/tmp/cache/assets/sprockets/v3.0/SS/SS3eYrFE6ghi5G_3POW760Y4BWo5OAaK5QjDrRGXOcs.cache +1 -0
  85. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{jj/jjdQCKZsewws2eRP7b2AVxRMvseOp8djjlDkB8UOtac.cache → Sq/SqjdxmYsdurpx9xk1ks32WEtf3D9_2BysE7RMxnOlmU.cache} +1 -1
  86. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{os/os0qWv3yDfep0e7vVSGTgfn_n3xc4C4QZgmMzLQn0Ks.cache → Ta/TaJnQ_l0sfSsDOyVoi-S-KZQ6vIwzBhmS6hmNA_n8ug.cache} +0 -0
  87. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{o6/o6ZQbq1aBFuVrE2xLbSbyZH2736sWM3Yj4ky9chT1bI.cache → X_/X_CxFXEdZo2C_in7FopAuAbplHn6W_V0SXyVQAMnTzI.cache} +0 -0
  88. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{sR/sRU-UT8qK_38kkiFDXKBPogO1oOZuSvJFH-pgqPL_Ms.cache → Yh/YhRWll6bzgYE6Dmc5fVKbHyv5om4y3-27UrWeUbQcGk.cache} +0 -0
  89. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{OD/ODHapE0li3WI1laVJoksoI5nmQd-8dDJXq_VxS4XefE.cache → _u/_uiA10I5HyJR94Tbc4a06CzdrQCAXX7Iz86WS5PKDQo.cache} +0 -0
  90. data/test/dummy/tmp/cache/assets/sprockets/v3.0/ai/ainCVF4dxXf85KoOLUSHvMMGg0qBNc7Vg-s-9v2ENJI.cache +4 -0
  91. data/test/dummy/tmp/cache/assets/sprockets/v3.0/cQ/cQqt0ri9GFvVnIs1pJUugIJs5tDuGQMMntW5CwpK6YY.cache +1 -0
  92. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{1n/1nStVaWIQEwAlksUpB1fInwq6v3TikPvuRaSwwCnCLQ.cache → e0/e0Y2Gz8ftaW2O0RjoN2iWguitI-JQZckScAZqlQYkng.cache} +0 -0
  93. data/test/dummy/tmp/cache/assets/sprockets/v3.0/iH/iHnlNNY3rfdbVqt8LZBfcDQYONR8dA8LmxA9fv5paro.cache +2 -0
  94. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{F0/F0oWBwLHpGFYNNtnGav-dJFH8rMDpNA5L8_aWm5Wq34.cache → ir/irxgIDPIY6Wz6fHWYp1dIrqAT1_8oa6-oiwqbFE_e-w.cache} +0 -0
  95. data/test/dummy/tmp/cache/assets/sprockets/v3.0/kn/knUtyVrg7nMIPldZa4MHhe7cC9eMchLZS4mc5HDWgDY.cache +1 -0
  96. data/test/dummy/tmp/cache/assets/sprockets/v3.0/ks/ks4DoIuUV6oUzoiXoPh7sW1k5ExNoUkR7ICjwuRnAT0.cache +1 -0
  97. data/test/dummy/tmp/cache/assets/sprockets/v3.0/m3/m375QQakjXd_2Trpvdot-i90nmeCCh0HWtzS66ZvxUU.cache +1 -0
  98. data/test/dummy/tmp/cache/assets/sprockets/v3.0/rQ/rQ_FgYkT2hdw9A6XmJKGgVb2dMtCrWWHwtS0X1_kdl0.cache +1 -0
  99. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{pw/pwiIhcWPwTuy5Miz5zqOQGYFx-K3TbaCVQ6ls7EBkk0.cache → rU/rUqFWwHqrT5jeIFCEezOPsMMZiAU7ri6RKBGS2ezAfg.cache} +0 -0
  100. data/test/dummy/tmp/cache/assets/sprockets/v3.0/{pO/pO8hHVwybv1ZJBdPa2YC6bjqmk020OYFl85TYxtmrWQ.cache → rq/rqf5-J6k_zLc9ZlUgMj28nviKtRsko3-iEp_m_uNeY0.cache} +0 -0
  101. data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  102. data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  103. data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  104. data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  105. data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  106. data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  107. metadata +143 -119
  108. data/app/assets/javascripts/quick_search/appstats.js +0 -8
  109. data/app/views/quick_search/appstats/_click_count_table.html.erb +0 -23
  110. data/app/views/quick_search/appstats/_top_spot_report_table.html.erb +0 -33
  111. data/app/views/quick_search/appstats/module_click_detail.html.erb +0 -32
  112. data/app/views/quick_search/appstats/top_spot_detail.html.erb +0 -35
  113. data/test/dummy/tmp/cache/assets/sprockets/v3.0/0_/0_w_ydOOEFQOoETxJa-IxKppQocaiDwM5V08djCPYso.cache +0 -3
  114. data/test/dummy/tmp/cache/assets/sprockets/v3.0/3m/3mWfBeEpm_bJvaPQZYvnnAVtE_2helOWh_Tv-cEflrk.cache +0 -5
  115. data/test/dummy/tmp/cache/assets/sprockets/v3.0/4s/4sg52OgD5l4qx4GnojCVxg2itqwqb-KRanEdmdMus3Y.cache +0 -0
  116. data/test/dummy/tmp/cache/assets/sprockets/v3.0/5U/5UPnsccuZFLk9OqbmFMvB55mbvGptApx9Z55HWjEN90.cache +0 -3
  117. data/test/dummy/tmp/cache/assets/sprockets/v3.0/9a/9acKNgyVD8_WK401tMy6Ce98g-LS4VpwFvGQdzCV9mI.cache +0 -0
  118. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Ba/BaymviI8cwAJQMOIzWCf6Lt-PM2sNFTvY9hQKsfDNNw.cache +0 -3
  119. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Dd/DdFHg3C_voetfvKGKX7t8PGzZlkO45-huHNtaou5rxg.cache +0 -1
  120. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Fp/FprCQeeZguht46WNSpA3Y3JHKehovFXrj63nDrvvwFE.cache +0 -1
  121. data/test/dummy/tmp/cache/assets/sprockets/v3.0/GJ/GJrfWiarIi0Dt6sdKVMfUF1cdZ6NbqZxXHhzNbFtpBw.cache +0 -1
  122. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Gp/GpoziOxxB-hisco0rl7Nnsnw0UWud7R9Yge2Q3o1eN4.cache +0 -2
  123. data/test/dummy/tmp/cache/assets/sprockets/v3.0/It/It5VkJv-53-LICLKZWWyp2WcbD86DGotAL0MY3hhfpg.cache +0 -6
  124. data/test/dummy/tmp/cache/assets/sprockets/v3.0/O6/O6dw148Xya7NYlsRFMEzJyvRcySM6iOL1jRUpdQo9B8.cache +0 -1
  125. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Oj/OjMkKTW7OkthjJAQHDID8GV30IZC9F0dJ1zGiFame9U.cache +0 -4
  126. data/test/dummy/tmp/cache/assets/sprockets/v3.0/SU/SUtZ_ltPqy9A9A-_zqNPhNPvjxCOnrdqXHSZv5S9mOk.cache +0 -1
  127. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Tv/TvZgmm69VEMHUJQIBoij61oXMkgzp6whVo7l8mJ9qpw.cache +0 -3
  128. data/test/dummy/tmp/cache/assets/sprockets/v3.0/YJ/YJZdK01T1KTS84Mb6p_2TasmJNZeGZc4FMtBPCKGr_M.cache +0 -3
  129. data/test/dummy/tmp/cache/assets/sprockets/v3.0/ah/ahhhd3ysbQmVDNzq2L_rb4t6rqqdnqeY6ujztBoZDA8.cache +0 -1
  130. data/test/dummy/tmp/cache/assets/sprockets/v3.0/gv/gvZKkbgiExbcQrT4eo0HB-j4JoDRAxdmDjKRxeEdK0Y.cache +0 -1
  131. data/test/dummy/tmp/cache/assets/sprockets/v3.0/hj/hj_r-O1QjHJIPcZVnlB_FGPdIRAi9NkxpT2oXDRIsGE.cache +0 -1
  132. data/test/dummy/tmp/cache/assets/sprockets/v3.0/o2/o2V1ahk8vFR6sXWNh5BB7w6MBj04zCyX-o1xFZONydE.cache +0 -1
  133. data/test/dummy/tmp/cache/assets/sprockets/v3.0/tE/tEO71oPPFXN_2c4p39nU0OiF9ARg13fjdQlnPqAx1KY.cache +0 -4
  134. data/test/dummy/tmp/cache/assets/sprockets/v3.0/tW/tWFRsAv4WWu2ch-x3AJZd3KOvBXG3MwH6Q6Xn-tFBtw.cache +0 -1
  135. data/test/dummy/tmp/cache/assets/sprockets/v3.0/x6/x6W754ylumshr3wBUHHmcgyWs8UR3Od7FaI6tqvjKmk.cache +0 -1
  136. data/test/dummy/tmp/cache/assets/sprockets/v3.0/yy/yyJ1P7RQciOiW8TgQiu1RMDdbWd4rqxd2BGU9HkY3L8.cache +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 945a512b7b05c32916eac69f31ae23e60cfd8234
4
- data.tar.gz: 59e982a4f144a577864dc288d76f9540126da661
3
+ metadata.gz: 9207dd409ceea89a6d272bda5ddf20c09bdcc778
4
+ data.tar.gz: ef41de760151b03afd8c87f68ca2ef76a5064eaa
5
5
  SHA512:
6
- metadata.gz: 375061e582cdd20159480052993b435759274b0bb2cf25c36deeb5bc48b6eb19e169fbd690b75e7a1b2a70362665c496b76ba4b1c65fae730952acf5fbbf78d8
7
- data.tar.gz: 37bf50794ae589a11245b7490b99e50d14514a5e97acfa6d468adbea0cb91e050322956bcb0f468b67e481c9be96ee82c8760b3fb7df013daf98097faac901e8
6
+ metadata.gz: c2a877cddf63641e6a4c4b82bc6727f2f795ba4019d5e1a5d0af10f63257bac07711fcc6d9ea9740b6a30152046cec0433060e21c33ad5d7ff0ce4f9723cfa78
7
+ data.tar.gz: f55f5a7b5b5184ace5305ed41c0f0b5f528876ca52224864fb049f530d589409fa364a817971e6c4f71aa1de83e3e09fbd8beecf835796143516be0e785236a2
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  QuickSearch is a customized federated search tool designed to connect people to a variety of library resources, services, and tools. QuickSearch is designed to complement and increase use of existing dedicated library resource discovery tools, such as the catalog, electronic resource search, and subject guides/portals, by directing people to them via an intuitive search interface. The aim of QuickSearch is to provide people with a quick and easy way to find the information they need.
11
11
 
12
- For more information about NCSU Libraries' QuickSearch and the history of the project, see: http://www.lib.ncsu.edu/reports/quicksearch
12
+ For more information about NCSU Libraries' QuickSearch and the history of the project, see: http://www.lib.ncsu.edu/projects/quicksearch
13
13
 
14
14
  For NCSU Libraries' implementation of QuickSearch, see:
15
15
  http://search.lib.ncsu.edu
@@ -39,8 +39,8 @@ development.
39
39
 
40
40
  - Rails 5.0
41
41
  - Ruby 2.2.2+ or 2.3.x
42
- - MySQL development headers (mysql-devel package on CentOS)
43
- - qtwebkit development headers (qtwebkit-devel package on CentOS)
42
+ - MySQL development headers (mysql-devel package on CentOS, libmysqlclient-dev on Ubuntu)
43
+ - qtwebkit development headers (qtwebkit-devel package on CentOS, libqtwebkit-dev on Ubuntu)
44
44
 
45
45
  #### Instructions
46
46
 
@@ -70,7 +70,7 @@ configuring QuickSearch, see: [Configuring QuickSearch](docs/configuration.md)
70
70
 
71
71
  ##### Start the server:
72
72
 
73
- bundle exec rails s
73
+ rails s
74
74
 
75
75
  ##### QuickSearch will be available at http://localhost:3000/
76
76
 
@@ -98,9 +98,9 @@ far. Feel free to submit a pull request to add to this list.
98
98
  |wikipedia |gem 'quick_search-wikipedia_searcher' |[quick_search-wikipedia_searcher](https://www.github.com/ncsu-libraries/quick_search-wikipedia_searcher) |
99
99
  |OpenLibrary |gem 'quick_search-open_library_searcher' |[quick_search-open_library_searcher](https://www.github.com/ncsu-libraries/quick_search-open_library_searcher) |
100
100
  |Placeholder |gem 'quick_search-placeholder_searcher' |[quick_search-placeholder_searcher](https://www.github.com/ncsu-libraries/quick_search-placeholder_searcher) |
101
- |Summon |Coming soon | |
101
+ |Summon |gem 'quick_search-summon_searcher' |[quick_search-summon_searcher](https://www.github.com/ncsu-libraries/quick_search-summon_searcher) |
102
102
  |NCSU Libraries' Catalog |Coming soon | |
103
-
103
+ |ContentDM |gem 'quick_search-contentdm' | [quick_search-contentdm](https://github.com/tulibraries/quick_search-contentdm) |
104
104
 
105
105
 
106
106
  ### Themes
@@ -150,6 +150,7 @@ QuickSearch (listed in alphabetical order)
150
150
  - Nushrat Khan
151
151
  - Cory Lown
152
152
  - Jason Ronallo
153
+ - Ryan West
153
154
 
154
155
  ## License
155
156
 
@@ -12,9 +12,18 @@
12
12
  //
13
13
  //= require jquery
14
14
  //= require jquery_ujs
15
+ //= require d3.min
16
+ //= require lodash.min
15
17
  //= require quick_search/google_analytics
16
18
  //= require quick_search/event_tracking
17
19
  //= require quick_search/xhr_search
18
20
  //= require quick_search/quicksearch
19
21
  //= require quick_search/appstats
22
+ //= require quick_search/appstats_clicks_overview
23
+ //= require quick_search/appstats_general_statistics
24
+ //= require quick_search/appstats_top_searches
25
+ //= require quick_search/appstats_spelling_suggestions
26
+ //= require quick_search/appstats_best_bets
27
+ //= require quick_search/appstats_sessions_overview
28
+ //= require quick_search/appstats_sessions_details
20
29
  <%= require_asset(QuickSearch::Engine::APP_CONFIG['theme']) %>
@@ -0,0 +1,73 @@
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
+ (function() {
4
+
5
+ $(function() {
6
+ $("#appstats-date-showhide").click(function() {
7
+ $("#appstats-date-filter").toggle();
8
+ });
9
+ });
10
+
11
+ })();
12
+
13
+ $(document).on("turbolinks:load", function() {
14
+ var dateFormat = "mm/dd/yy",
15
+ from = $("#from").datepicker({
16
+ defaultDate: "-180d",
17
+ maxDate: 0,
18
+ changeMonth: true,
19
+ changeYear: true,
20
+ numberOfMonths: 1
21
+ })
22
+ .on("change", function() {
23
+ to.datepicker("option", "minDate", getDate(this));
24
+ }),
25
+ to = $("#to").datepicker({
26
+ defaultDate: "+0",
27
+ maxDate: 0,
28
+ changeMonth: true,
29
+ changeYear: true,
30
+ numberOfMonths: 1
31
+ })
32
+ .on("change", function() {
33
+ from.datepicker("option", "maxDate", getDate(this));
34
+ });
35
+
36
+ $("#from").datepicker('setDate', new Date(new Date() - 180*24*60*60*1000));
37
+ $("#to").datepicker('setDate', new Date());
38
+ var number = document.getElementById("num");
39
+ if (number) {
40
+ number.onkeydown = function(e) {
41
+ console.log(e.keyCode);
42
+ if(!((e.keyCode > 95 && e.keyCode < 106) || (e.keyCode > 47 && e.keyCode < 58) || e.keyCode == 8)) {
43
+ return false;
44
+ }
45
+ };
46
+ number.value = "20";
47
+ }
48
+
49
+ function getDate(element) {
50
+ var date;
51
+ try {
52
+ date = $.datepicker.parseDate(dateFormat, element.value);
53
+ } catch (error) {
54
+ date = null;
55
+ }
56
+
57
+ return date;
58
+ }
59
+ });
60
+
61
+ function lighten() {
62
+ var id = "#" + event.target.id;
63
+ if (id != "#") {
64
+ d3.select(id).select(".areaMini").style("fill", "lightsteelblue");
65
+ }
66
+ }
67
+
68
+ function darken() {
69
+ var id = "#" + event.target.id;
70
+ if (id != "#") {
71
+ d3.select(id).select(".areaMini").style("fill", "steelblue");
72
+ }
73
+ }
@@ -0,0 +1,905 @@
1
+ <% url_helper = QuickSearch::Engine.routes.url_helpers %>
2
+
3
+ (function() {
4
+ var Globals = {};
5
+
6
+ $(document).on("turbolinks:load", function() {
7
+ if ($("#graph_best_bets").length) {
8
+ d3.select("#bbIconContainer").append("i")
9
+ .attr("class", "fa fa-spinner fa-spin fa-5x fa-fw big-icon")
10
+ .attr("id", "bbIcon");
11
+ // Redraw graph if the date range is changed
12
+ document.getElementById("dateButton").addEventListener("click", function() {
13
+ var from = $("#from").datepicker("getDate");
14
+ var to = new Date($("#to").datepicker("getDate").getTime() + 1000*60*60*24);
15
+ document.getElementById("numDays").innerHTML = "" + parseInt((to-from)/(1000*60*60*24));
16
+ if (document.getElementById("num").value>200) { document.getElementById("num").value = "20"; }
17
+ $.ajax({
18
+ type: "GET",
19
+ contentType: "application/json; charset=utf-8",
20
+ url: '<%= url_helper.data_best_bets_path %>',
21
+ dataType: "json",
22
+ data: {
23
+ "start_date": from,
24
+ "end_date": to,
25
+ "num_results": (document.getElementById("num").value>200) ? Globals.Data.length : document.getElementById("num").value
26
+ },
27
+ success: function(bestBetDataSet) {
28
+ var bestBetData = _.cloneDeep(bestBetDataSet);
29
+ Globals.OriginalData = bestBetData;
30
+ Globals.Data = bestBetData;
31
+ sort_search_data(Globals.Sort);
32
+ },
33
+ error: function(result) {
34
+ error();
35
+ }
36
+ });
37
+ });
38
+ $.ajax({
39
+ type: "GET",
40
+ contentType: "application/json; charset=utf-8",
41
+ url: '<%= url_helper.data_best_bets_path %>',
42
+ dataType: "json",
43
+ success: function(bestBetDataSet) {
44
+ d3.select("#bbIcon").transition().duration(250)
45
+ .style("opacity", .000001)
46
+ .remove();
47
+ var bestBetData = _.cloneDeep(bestBetDataSet);
48
+ Globals.OriginalData = bestBetData;
49
+ Globals.Data = bestBetData;
50
+ Globals.Sort = 0;
51
+ Globals.Height = 450;
52
+ draw_graph_best_bets(false);
53
+ },
54
+ error: function(result) {
55
+ error();
56
+ }
57
+ });
58
+ }
59
+ });
60
+
61
+ function error() {
62
+ console.log("Error retrieving data");
63
+ }
64
+
65
+ function draw_graph_best_bets(resetExpanded) {
66
+ if ($("#graph_best_bets").length) {
67
+ // General Variables
68
+ var svg; // SVG to contain graph
69
+ var dataInt; // Internal reference to graph dataset
70
+ var sortStates; // Array indicating the currently selected sort state
71
+ var key; // Key function to maintain object constancy
72
+ var triangle; // Triangle object for sort buttons
73
+ // Dimension Variables
74
+ var margin; // Blank space around edge of SVG left as margin
75
+ var rowHeight; // Height of each row of data
76
+ var width; // Drawable width of SVG (not including margins)
77
+ var height; // Drawable height of SVG (not including margins)
78
+ var oldHeight; // Previous height of SVG
79
+ var textPadding; // Padding around text elements
80
+ var barHeight; // Height of data bars
81
+ var rankWidth; // Width of rank column
82
+ var categoryWidth; // Width of category column
83
+ var serveWidth; // Width of serves column
84
+ var clickWidth; // Width of click column
85
+ var ratioWidth; // Width of ratio column
86
+ var childOffset; // Horizontal offset for child elements drawn on element expansion
87
+ var serveBarStart; // Beginning x for serves data bar
88
+ var clickBarStart; // Beginning x for click data bar
89
+ var ratioBarStart; // Beginning x for ratio data bar
90
+ var triangleSpacing;// Space around triangular sort buttons
91
+ // Scale Variables
92
+ var serveScale; // Scale for serve count data bar
93
+ var clickScale; // Scale for click count data bar
94
+ var ratioScale; // Scale for ratio data bar
95
+ // Selection Variables
96
+ var groupEnter; // Entry selection for gAll
97
+ var gAll; // Group containing all elements
98
+ var headGroupEnter; // Entry selection for headRow
99
+ var headRow; // Group containing all head elements
100
+ var headEnter; // Entry selection for all head elements
101
+ var triangles; // Sort triangles
102
+ var dataGroupEnter; // Entry selection for dataRows
103
+ var dataRows; // Group containing all data elements
104
+ var dataRects; // Background rectangles
105
+ var dataRanks; // Rank text
106
+ var dataLabels; // Label text
107
+ var dataServes; // Serve text
108
+ var dataClicks; // Click text
109
+ var dataRatios; // Ratio text
110
+ var serveBar; // Serve count data bar
111
+ var clickBar; // Click count data bar
112
+ var ratioBar; // Ratio data bar
113
+ var lightBorder; // Light border (surrounds all elements)
114
+ var darkBorder; // Dark border (surrounds data elements)
115
+
116
+ // Initialize General Variables ///////////////////////////////////////////////////////////
117
+ svg = d3.select("#graph_best_bets");
118
+
119
+ dataInt = Globals.Data;
120
+
121
+ sortStates = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
122
+ sortStates[Globals.Sort] = 1;
123
+
124
+ key = function(d) {
125
+ return d.key;
126
+ };
127
+
128
+ triangle = d3.symbol().type(d3.symbolTriangle).size(20);
129
+
130
+ // Initialize Dimension Variables /////////////////////////////////////////////////////////
131
+ margin = {
132
+ top: 20,
133
+ right: 20,
134
+ bottom: 20,
135
+ left: 20
136
+ };
137
+ rowHeight = 35;
138
+ width = +svg.attr("width") - margin.left - margin.right;
139
+ height = (dataInt.length + 1) * rowHeight;
140
+ oldHeight = Globals.Height;
141
+ textPadding = 5;
142
+ barHeight = 25;
143
+ rankWidth = 2 * width / 20;
144
+ categoryWidth = 5 * width / 20;
145
+ serveWidth = 4 * width / 20;
146
+ clickWidth = 4 * width / 20;
147
+ ratioWidth = 5 * width / 20;
148
+ childOffset = 20;
149
+ serveBarStart = rankWidth + categoryWidth + 4 * textPadding + 13 * d3.max(dataInt, function(d) {
150
+ return d.serves.toString().length;
151
+ });
152
+ clickBarStart = rankWidth + categoryWidth + serveWidth + 4 * textPadding + 13 * d3.max(dataInt, function(d) {
153
+ return d.clicks.toString().length;
154
+ });
155
+ ratioBarStart = rankWidth + categoryWidth + serveWidth + clickWidth + 4 * textPadding + 13 * d3.max(dataInt, function(d) {
156
+ return d.ratio.toString().length;
157
+ });
158
+ triangleSpacing = 15;
159
+
160
+ // Initialize Scale Variables /////////////////////////////////////////////////////////////
161
+ serveScale = d3.scaleLinear().rangeRound([0, rankWidth + categoryWidth + serveWidth - serveBarStart - triangleSpacing]);
162
+ serveScale.domain([0, d3.max(dataInt, function(d) {
163
+ return d.serves;
164
+ })]);
165
+
166
+ clickScale = d3.scaleLinear().rangeRound([0, rankWidth + categoryWidth + serveWidth + clickWidth - clickBarStart - triangleSpacing]);
167
+ clickScale.domain([0, d3.max(dataInt, function(d) {
168
+ return d.clicks;
169
+ })]);
170
+
171
+ ratioScale = d3.scaleLinear().rangeRound([0, rankWidth + categoryWidth + serveWidth + clickWidth + ratioWidth - ratioBarStart - triangleSpacing]);
172
+ ratioScale.domain([0, d3.max(dataInt, function(d) {
173
+ return d.ratio;
174
+ })]);
175
+
176
+ // Misc ///////////////////////////////////////////////////////////////////////////////////
177
+ // Transition the svg height depending on previous height
178
+ svg.transition().delay(function(d) {
179
+ if (oldHeight > height) {
180
+ return 500;
181
+ } else {
182
+ return 0;
183
+ }
184
+ })
185
+ .duration(750)
186
+ .attr("height", height + margin.top + margin.bottom)
187
+ .on("end", function(d) {
188
+ Globals.Height = height;
189
+ });
190
+
191
+ // Reset expanded categories when after a resort
192
+ if (resetExpanded) {
193
+ dataInt.forEach(function(d) {
194
+ d.expanded = 0;
195
+ });
196
+ }
197
+
198
+ // Make overarching group /////////////////////////////////////////////////////////////////
199
+ groupEnter = svg.selectAll("g").data([dataInt]).enter();
200
+
201
+ groupEnter.append("g")
202
+ .attr("class", "gAll");
203
+
204
+ gAll = svg.select(".gAll")
205
+ .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
206
+
207
+ // Make header row ////////////////////////////////////////////////////////////////////////
208
+ headGroupEnter = gAll.selectAll(".headRow").data([dataInt]).enter();
209
+
210
+ headGroupEnter.append("g")
211
+ .attr("class", "headRow");
212
+
213
+ headRow = gAll.select(".headRow")
214
+ .attr("transform", "translate(0,0)");
215
+
216
+ headEnter = headRow.selectAll("rect").data([dataInt]).enter();
217
+
218
+ // Make background rect
219
+ headEnter.append("rect")
220
+ .attr("class", "darkBar")
221
+ .attr("x", 0)
222
+ .attr("y", 0)
223
+ .attr("width", width)
224
+ .attr("height", rowHeight);
225
+
226
+ // Make rank text & sort rect
227
+ headEnter.append("text")
228
+ .text("Rank")
229
+ .attr("class", "headText")
230
+ .attr("transform", "translate(" + textPadding + "," + (rowHeight / 2 + textPadding) + ")");
231
+
232
+ headEnter.append("rect")
233
+ .attr("class", "sortRect")
234
+ .attr("x", (rankWidth - 2 * triangleSpacing))
235
+ .attr("y", 0)
236
+ .attr("width", 2 * triangleSpacing)
237
+ .attr("height", rowHeight)
238
+ .on("click", function(d) {
239
+ if (Globals.Sort == 0) {
240
+ sort_search_data(1);
241
+ } else {
242
+ sort_search_data(0);
243
+ }
244
+ });
245
+
246
+ // Make category text & sort rect
247
+ headEnter.append("text")
248
+ .text("Best-Bet")
249
+ .attr("class", "headText")
250
+ .attr("transform", "translate(" + (rankWidth + textPadding) + "," + (rowHeight / 2 + textPadding) + ")");
251
+
252
+ headEnter.append("rect")
253
+ .attr("class", "sortRect")
254
+ .attr("x", (rankWidth + categoryWidth - 2 * triangleSpacing))
255
+ .attr("y", 0)
256
+ .attr("width", 2 * triangleSpacing)
257
+ .attr("height", rowHeight)
258
+ .on("click", function(d) {
259
+ if (Globals.Sort == 2) {
260
+ sort_search_data(3);
261
+ } else {
262
+ sort_search_data(2);
263
+ }
264
+ });
265
+
266
+ // Make serve text & sort rect
267
+ headEnter.append("text")
268
+ .text("Serve Count")
269
+ .attr("class", "headText")
270
+ .attr("transform", "translate(" + (rankWidth + categoryWidth + textPadding) + "," + (rowHeight / 2 + textPadding) + ")");
271
+
272
+ headEnter.append("rect")
273
+ .attr("class", "sortRect")
274
+ .attr("x", (rankWidth + categoryWidth + serveWidth - 2 * triangleSpacing))
275
+ .attr("y", 0)
276
+ .attr("width", 2 * triangleSpacing)
277
+ .attr("height", rowHeight)
278
+ .on("click", function(d) {
279
+ if (Globals.Sort == 4) {
280
+ sort_search_data(5);
281
+ } else {
282
+ sort_search_data(4);
283
+ }
284
+ });
285
+
286
+ // Make click text & sort rect
287
+ headEnter.append("text")
288
+ .text("Click Count")
289
+ .attr("class", "headText")
290
+ .attr("transform", "translate(" + (rankWidth + categoryWidth + serveWidth + textPadding) + "," + (rowHeight / 2 + textPadding) + ")");
291
+
292
+ headEnter.append("rect")
293
+ .attr("class", "sortRect")
294
+ .attr("x", (rankWidth + categoryWidth + serveWidth + clickWidth - 2 * triangleSpacing))
295
+ .attr("y", 0)
296
+ .attr("width", 2 * triangleSpacing)
297
+ .attr("height", rowHeight)
298
+ .on("click", function(d) {
299
+ if (Globals.Sort == 6) {
300
+ sort_search_data(7);
301
+ } else {
302
+ sort_search_data(6);
303
+ }
304
+ });
305
+
306
+ // Make ratio text & sort rect
307
+ headEnter.append("text")
308
+ .text("Click/Serve")
309
+ .attr("class", "headText")
310
+ .attr("transform", "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth + textPadding) + "," + (rowHeight / 2 + textPadding) + ")");
311
+
312
+ headEnter.append("rect")
313
+ .attr("class", "sortRect")
314
+ .attr("x", (rankWidth + categoryWidth + serveWidth + clickWidth + ratioWidth - 2 * triangleSpacing))
315
+ .attr("y", 0)
316
+ .attr("width", 2 * triangleSpacing)
317
+ .attr("height", rowHeight)
318
+ .on("click", function(d) {
319
+ if (Globals.Sort == 8) {
320
+ sort_search_data(9);
321
+ } else {
322
+ sort_search_data(8);
323
+ }
324
+ });
325
+
326
+ // Make sort triangles
327
+ triangles = headRow.selectAll("path").data(sortStates);
328
+
329
+ triangles.enter().append("path")
330
+ .attr("d", triangle)
331
+ .attr("pointer-events", "none")
332
+ .attr("transform", function(d, i) {
333
+ if (i == 0) {
334
+ return "translate(" + (rankWidth - triangleSpacing) + "," + (rowHeight / 2 - textPadding) + ")";
335
+ } else if (i == 1) {
336
+ return "translate(" + (rankWidth - triangleSpacing) + "," + (rowHeight / 2 + textPadding) + ") rotate(180)";
337
+ } else if (i == 2) {
338
+ return "translate(" + (rankWidth + categoryWidth - triangleSpacing) + "," + (rowHeight / 2 - textPadding) + ")";
339
+ } else if (i == 3) {
340
+ return "translate(" + (rankWidth + categoryWidth - triangleSpacing) + "," + (rowHeight / 2 + textPadding) + ") rotate(180)";
341
+ } else if (i == 4) {
342
+ return "translate(" + (rankWidth + categoryWidth + serveWidth - triangleSpacing) + "," + (rowHeight / 2 - textPadding) + ")";
343
+ } else if (i == 5) {
344
+ return "translate(" + (rankWidth + categoryWidth + serveWidth - triangleSpacing) + "," + (rowHeight / 2 + textPadding) + ") rotate(180)";
345
+ } else if (i == 6) {
346
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth - triangleSpacing) + "," + (rowHeight / 2 - textPadding) + ")";
347
+ } else if (i == 7) {
348
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth - triangleSpacing) + "," + (rowHeight / 2 + textPadding) + ") rotate(180)";
349
+ } else if (i == 8) {
350
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth + ratioWidth - triangleSpacing) + "," + (rowHeight / 2 - textPadding) + ")";
351
+ } else if (i == 9) {
352
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth + ratioWidth - triangleSpacing) + "," + (rowHeight / 2 + textPadding) + ") rotate(180)";
353
+ }
354
+ })
355
+ .merge(triangles)
356
+ .transition().duration(500)
357
+ .attr("fill", function(d, i) {
358
+ if (d) {
359
+ return "steelblue";
360
+ } else {
361
+ return "lightsteelblue";
362
+ }
363
+ });
364
+
365
+ // Make data rows /////////////////////////////////////////////////////////////////////////
366
+ dataGroupEnter = gAll.selectAll(".dataRows").data([dataInt]).enter();
367
+
368
+ dataGroupEnter.append("g")
369
+ .attr("class", "dataRows");
370
+
371
+ dataRows = gAll.select(".dataRows")
372
+ .attr("transform", "translate(0,0)");
373
+
374
+ // Make background rects
375
+ dataRects = dataRows.selectAll(".darkBar, .lightBar").data(dataInt, key);
376
+
377
+ dataRects.transition().delay(function(d) {
378
+ if (oldHeight > height) {
379
+ return 500;
380
+ } else {
381
+ return 0;
382
+ }
383
+ })
384
+ .duration(750)
385
+ .attr("y", function(d, i) {
386
+ return (i + 1) * rowHeight;
387
+ });
388
+ dataRects.exit().remove();
389
+ dataRects.enter().append("rect")
390
+ .style("opacity", .000001)
391
+ .attr("class", function(d, i) {
392
+ if (d.parent) {
393
+ return "lightBar";
394
+ } else {
395
+ return "darkBar";
396
+ }
397
+ })
398
+ .attr("x", 0)
399
+ .attr("y", function(d, i) {
400
+ return (i + 1) * rowHeight;
401
+ })
402
+ .attr("width", width)
403
+ .attr("height", rowHeight)
404
+ .transition().delay(750).duration(500)
405
+ .style("opacity", 1);
406
+
407
+ // Make rank text
408
+ dataRanks = dataRows.selectAll(".dataRank").data(dataInt, key);
409
+
410
+ dataRanks.transition().delay(function(d) {
411
+ if (oldHeight > height) {
412
+ return 500;
413
+ } else {
414
+ return 0;
415
+ }
416
+ })
417
+ .duration(750)
418
+ .attr("transform", function(d, i) {
419
+ if (!d.parent) {
420
+ return "translate(" + textPadding + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
421
+ } else {
422
+ return "translate(" + (textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
423
+ }
424
+ });
425
+ dataRanks.exit().transition().duration(500)
426
+ .style("opacity", .000001)
427
+ .remove();
428
+ dataRanks.enter().append("text")
429
+ .style("opacity", .000001)
430
+ .text(function(d) {
431
+ return d.rank;
432
+ })
433
+ .attr("class", "dataRank")
434
+ .attr("transform", function(d, i) {
435
+ if (!d.parent) {
436
+ return "translate(" + textPadding + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
437
+ } else {
438
+ return "translate(" + (textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
439
+ }
440
+ })
441
+ .transition().delay(750).duration(500)
442
+ .style("opacity", 1);
443
+
444
+ // Make label text
445
+ dataLabels = dataRows.selectAll(".dataLabel").data(dataInt, key);
446
+
447
+ dataLabels.transition().delay(function(d) {
448
+ if (oldHeight > height) {
449
+ return 500;
450
+ } else {
451
+ return 0;
452
+ }
453
+ })
454
+ .duration(750)
455
+ .attr("transform", function(d, i) {
456
+ if (!d.parent) {
457
+ return "translate(" + (rankWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
458
+ } else {
459
+ return "translate(" + (rankWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
460
+ }
461
+ })
462
+ .text(function(d) {
463
+ if (d.label.length > 25) {
464
+ return d.label.slice(0, 20) + "...";
465
+ } else {
466
+ return d.label;
467
+ }
468
+ });
469
+ dataLabels.exit().transition().duration(500)
470
+ .style("opacity", .000001)
471
+ .remove();
472
+ dataLabels.enter().append("text")
473
+ .style("opacity", .000001)
474
+ .text(function(d) {
475
+ if (d.label.length > 25) {
476
+ return d.label.slice(0, 20) + "...";
477
+ } else {
478
+ return d.label;
479
+ }
480
+ })
481
+ .attr("class", "dataLabel")
482
+ .attr("transform", function(d, i) {
483
+ if (!d.parent) {
484
+ return "translate(" + (rankWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
485
+ } else {
486
+ return "translate(" + (rankWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
487
+ }
488
+ })
489
+ .transition().delay(750).duration(500)
490
+ .style("opacity", 1);
491
+
492
+ // Make serve text
493
+ dataServes = dataRows.selectAll(".dataServe").data(dataInt, key);
494
+
495
+ dataServes.transition().delay(function(d) {
496
+ if (oldHeight > height) {
497
+ return 500;
498
+ } else {
499
+ return 0;
500
+ }
501
+ })
502
+ .duration(750)
503
+ .attr("transform", function(d, i) {
504
+ if (!d.parent) {
505
+ return "translate(" + (rankWidth + categoryWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
506
+ } else {
507
+ return "translate(" + (rankWidth + categoryWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
508
+ }
509
+ })
510
+ .text(function(d) {
511
+ return d.serves;
512
+ });
513
+ dataServes.exit().transition().duration(500)
514
+ .style("opacity", .000001)
515
+ .remove();
516
+ dataServes.enter().append("text")
517
+ .style("opacity", .000001)
518
+ .text(function(d) {
519
+ return d.serves;
520
+ })
521
+ .attr("class", "dataServe")
522
+ .attr("transform", function(d, i) {
523
+ if (!d.parent) {
524
+ return "translate(" + (rankWidth + categoryWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
525
+ } else {
526
+ return "translate(" + (rankWidth + categoryWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
527
+ }
528
+ })
529
+ .transition().delay(750).duration(500)
530
+ .style("opacity", 1);
531
+
532
+ // Make click text
533
+ dataClicks = dataRows.selectAll(".dataClick").data(dataInt, key);
534
+
535
+ dataClicks.transition().delay(function(d) {
536
+ if (oldHeight > height) {
537
+ return 500;
538
+ } else {
539
+ return 0;
540
+ }
541
+ })
542
+ .duration(750)
543
+ .attr("transform", function(d, i) {
544
+ if (!d.parent) {
545
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
546
+ } else {
547
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
548
+ }
549
+ })
550
+ .text(function(d) {
551
+ return d.clicks;
552
+ });
553
+ dataClicks.exit().transition().duration(500)
554
+ .style("opacity", .000001)
555
+ .remove();
556
+ dataClicks.enter().append("text")
557
+ .style("opacity", .000001)
558
+ .text(function(d) {
559
+ return d.clicks;
560
+ })
561
+ .attr("class", "dataClick")
562
+ .attr("transform", function(d, i) {
563
+ if (!d.parent) {
564
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
565
+ } else {
566
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
567
+ }
568
+ })
569
+ .transition().delay(750).duration(500)
570
+ .style("opacity", 1);
571
+
572
+ // Make ratio text
573
+ dataRatios = dataRows.selectAll(".dataRatio").data(dataInt, key);
574
+
575
+ dataRatios.transition().delay(function(d) {
576
+ if (oldHeight > height) {
577
+ return 500;
578
+ } else {
579
+ return 0;
580
+ }
581
+ })
582
+ .duration(750)
583
+ .attr("transform", function(d, i) {
584
+ if (!d.parent) {
585
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
586
+ } else {
587
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
588
+ }
589
+ })
590
+ .text(function(d) {
591
+ return d.ratio + "%";
592
+ });
593
+ dataRatios.exit().transition().duration(500)
594
+ .style("opacity", .000001)
595
+ .remove();
596
+ dataRatios.enter().append("text")
597
+ .style("opacity", .000001)
598
+ .text(function(d) {
599
+ return d.ratio + "%";
600
+ })
601
+ .attr("class", "dataRatio")
602
+ .attr("transform", function(d, i) {
603
+ if (!d.parent) {
604
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth + textPadding) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
605
+ } else {
606
+ return "translate(" + (rankWidth + categoryWidth + serveWidth + clickWidth + textPadding + childOffset) + "," + Math.floor((i + 1) * rowHeight + rowHeight / 2 + textPadding) + ")";
607
+ }
608
+ })
609
+ .transition().delay(750).duration(500)
610
+ .style("opacity", 1);
611
+
612
+ // Make serve bars
613
+ serveBar = dataRows.selectAll(".serveBar, .lightServeBar").data(dataInt, key);
614
+
615
+ serveBar.transition().delay(function(d) {
616
+ if (oldHeight > height) {
617
+ return 500;
618
+ } else {
619
+ return 0;
620
+ }
621
+ })
622
+ .duration(750)
623
+ .attr("y", function(d, i) {
624
+ return (i + 1) * rowHeight + (rowHeight - barHeight) / 2;
625
+ })
626
+ .attr("width", function(d) {
627
+ return serveScale(d.serves);
628
+ })
629
+ .attr("x", function(d) {
630
+ return serveBarStart;
631
+ });
632
+ serveBar.exit().transition().duration(500)
633
+ .attr("width", 0)
634
+ .remove();
635
+ serveBar.enter().append("rect")
636
+ .attr("class", function(d) {
637
+ if (!d.parent) {
638
+ return "serveBar";
639
+ } else {
640
+ return "lightServeBar";
641
+ }
642
+ })
643
+ .style("cursor", function(d) {
644
+ if (!d.parent) {
645
+ return "pointer";
646
+ } else {
647
+ return "auto";
648
+ }
649
+ })
650
+ .attr("x", function(d) {
651
+ return serveBarStart;
652
+ })
653
+ .attr("y", function(d, i) {
654
+ return (i + 1) * rowHeight + (rowHeight - barHeight) / 2;
655
+ })
656
+ .attr("width", function(d) {
657
+ return 0;
658
+ })
659
+ .attr("height", barHeight)
660
+ .on("click", function(d) { expand(d) })
661
+ .transition().delay(750).duration(500)
662
+ .attr("width", function(d) {
663
+ return serveScale(d.serves);
664
+ });
665
+
666
+ // Make click bars
667
+ clickBar = dataRows.selectAll(".clickBar, .lightClickBar").data(dataInt, key);
668
+
669
+ clickBar.transition().delay(function(d) {
670
+ if (oldHeight > height) {
671
+ return 500;
672
+ } else {
673
+ return 0;
674
+ }
675
+ })
676
+ .duration(750)
677
+ .attr("y", function(d, i) {
678
+ return (i + 1) * rowHeight + (rowHeight - barHeight) / 2;
679
+ })
680
+ .attr("width", function(d) {
681
+ return clickScale(d.clicks);
682
+ })
683
+ .attr("x", function(d) {
684
+ return clickBarStart;
685
+ });
686
+ clickBar.exit().transition().duration(500)
687
+ .attr("width", 0)
688
+ .remove();
689
+ clickBar.enter().append("rect")
690
+ .attr("class", function(d) {
691
+ if (!d.parent) {
692
+ return "clickBar";
693
+ } else {
694
+ return "lightClickBar";
695
+ }
696
+ })
697
+ .style("cursor", function(d) {
698
+ if (!d.parent) {
699
+ return "pointer";
700
+ } else {
701
+ return "auto";
702
+ }
703
+ })
704
+ .attr("x", function(d) {
705
+ return clickBarStart;
706
+ })
707
+ .attr("y", function(d, i) {
708
+ return (i + 1) * rowHeight + (rowHeight - barHeight) / 2;
709
+ })
710
+ .attr("width", function(d) {
711
+ return 0;
712
+ })
713
+ .attr("height", barHeight)
714
+ .on("click", function(d) { expand(d) })
715
+ .transition().delay(750).duration(500)
716
+ .attr("width", function(d) {
717
+ return clickScale(d.clicks);
718
+ });
719
+
720
+ // Make ratio bars
721
+ ratioBar = dataRows.selectAll(".ratioBar , .lightRatioBar").data(dataInt, key);
722
+
723
+ ratioBar.transition().delay(function(d) {
724
+ if (oldHeight > height) {
725
+ return 500;
726
+ } else {
727
+ return 0;
728
+ }
729
+ })
730
+ .duration(750)
731
+ .attr("y", function(d, i) {
732
+ return (i + 1) * rowHeight + (rowHeight - barHeight) / 2;
733
+ })
734
+ .attr("width", function(d) {
735
+ return ratioScale(d.ratio);
736
+ })
737
+ .attr("x", function(d) {
738
+ return ratioBarStart;
739
+ });
740
+ ratioBar.exit().transition().duration(500)
741
+ .attr("width", 0)
742
+ .remove();
743
+ ratioBar.enter().append("rect")
744
+ .attr("class", function(d) {
745
+ if (!d.parent) {
746
+ return "ratioBar";
747
+ } else {
748
+ return "lightRatioBar";
749
+ }
750
+ })
751
+ .style("cursor", function(d) {
752
+ if (!d.parent) {
753
+ return "pointer";
754
+ } else {
755
+ return "auto";
756
+ }
757
+ })
758
+ .attr("x", function(d) {
759
+ return ratioBarStart;
760
+ })
761
+ .attr("y", function(d, i) {
762
+ return (i + 1) * rowHeight + (rowHeight - barHeight) / 2;
763
+ })
764
+ .attr("width", function(d) {
765
+ return 0;
766
+ })
767
+ .attr("height", barHeight)
768
+ .on("click", function(d) { expand(d) })
769
+ .transition().delay(750).duration(500)
770
+ .attr("width", function(d) {
771
+ return ratioScale(d.ratio);
772
+ });
773
+
774
+ // Add borders ////////////////////////////////////////////////////////////////////////////
775
+ lightBorder = gAll.selectAll(".lightBorder").data([dataInt]);
776
+
777
+ lightBorder.transition().delay(function(d) {
778
+ if (oldHeight > height) {
779
+ return 500;
780
+ } else {
781
+ return 0;
782
+ }
783
+ })
784
+ .duration(750)
785
+ .attr("height", height);
786
+ lightBorder.exit().remove();
787
+ lightBorder.enter().append("rect")
788
+ .attr("class", "lightBorder")
789
+ .attr("x", 0)
790
+ .attr("y", 0)
791
+ .attr("width", width)
792
+ .attr("height", height);
793
+
794
+ darkBorder = gAll.selectAll(".darkBorder").data([dataInt]);
795
+
796
+ darkBorder.transition().delay(function(d) {
797
+ if (oldHeight > height) {
798
+ return 500;
799
+ } else {
800
+ return 0;
801
+ }
802
+ })
803
+ .duration(750)
804
+ .attr("height", height - rowHeight);
805
+ darkBorder.exit().remove();
806
+ darkBorder.enter().append("rect")
807
+ .attr("class", "darkBorder")
808
+ .attr("x", 0)
809
+ .attr("y", rowHeight)
810
+ .attr("width", width)
811
+ .attr("height", height - rowHeight);
812
+ }
813
+ }
814
+
815
+ function expand(d) {
816
+ if (!d.expanded && !d.parent) {
817
+ d.expanded = 1;
818
+ load_details(d.label);
819
+ } else if (d.expanded && !d.parent) {
820
+ d.expanded = 0;
821
+ remove_details(d.label);
822
+ }
823
+ }
824
+
825
+ function sort_search_data(sort) {
826
+ if (Globals.Data.length != Globals.OriginalData.length) { // If details are present, those must be removed before resort
827
+ // Go back to original data set (no details)
828
+ Globals.Data = Globals.OriginalData.sort(function(a,b) { return determine_sort(Globals.Sort, a, b) });
829
+
830
+ // Resort data after delay
831
+ setTimeout(function() {
832
+ Globals.Data = Globals.OriginalData.sort(function(a,b) { return determine_sort(sort, a, b) });
833
+ draw_graph_best_bets(true);
834
+ }, 750);
835
+ } else { // Otherwise, you can just resort the data
836
+ Globals.Data = Globals.OriginalData.sort(function(a,b) { return determine_sort(sort, a, b) });
837
+ }
838
+
839
+ // Update global sort
840
+ Globals.Sort = sort;
841
+ draw_graph_best_bets(true);
842
+ }
843
+
844
+ function determine_sort(sort, a, b) {
845
+ switch(sort) {
846
+ case 0: return d3.ascending(a.rank,b.rank);
847
+ case 1: return d3.ascending(b.rank,a.rank);
848
+ case 2: return d3.ascending(a.label,b.label);
849
+ case 3: return d3.ascending(b.label,a.label);
850
+ case 4: return d3.ascending(a.serves,b.serves);
851
+ case 5: return d3.ascending(b.serves,a.serves);
852
+ case 6: return d3.ascending(a.clicks,b.clicks);
853
+ case 7: return d3.ascending(b.clicks,a.clicks);
854
+ case 8: return d3.ascending(a.ratio,b.ratio);
855
+ case 9: return d3.ascending(b.ratio,a.ratio);
856
+ }
857
+ }
858
+
859
+ function load_details(item) {
860
+ var updateData = _.cloneDeep(Globals.Data);
861
+ var from = $("#from").datepicker("getDate");
862
+ var to = new Date($("#to").datepicker("getDate").getTime() + 1000*60*60*24);
863
+ $.ajax({
864
+ type: "GET",
865
+ contentType: "application/json; charset=utf-8",
866
+ url: '<%= url_helper.data_best_bets_details_path %>',
867
+ dataType: "json",
868
+ data: {
869
+ "item": item,
870
+ "start_date": from,
871
+ "end_date": to
872
+ },
873
+ success: function(detailSet) {
874
+ var detailData = _.cloneDeep(detailSet);
875
+ var index = _.findIndex(updateData, {
876
+ "label": item
877
+ });
878
+ updateData = _.concat(_.slice(updateData, 0, index + 1), detailData, _.slice(updateData, index + 1, updateData.length));
879
+
880
+ Globals.Data = updateData;
881
+ draw_graph_best_bets(false);
882
+ },
883
+ error: function(result) {
884
+ error();
885
+ }
886
+ });
887
+ }
888
+
889
+ function remove_details(item) {
890
+ var updateData = _.cloneDeep(Globals.Data);
891
+ var startIndex = _.findIndex(updateData, {
892
+ "label": item
893
+ });
894
+ var endIndex = startIndex + 1;
895
+
896
+ while (endIndex < updateData.length && updateData[endIndex].parent) {
897
+ endIndex++;
898
+ }
899
+ updateData = _.concat(_.slice(updateData, 0, startIndex + 1), _.slice(updateData, endIndex, updateData.length));
900
+
901
+ Globals.Data = updateData;
902
+ draw_graph_best_bets(false);
903
+ }
904
+
905
+ })();