quick_search-core 0.1.1 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +7 -6
- data/app/assets/javascripts/quick_search.js.erb +9 -0
- data/app/assets/javascripts/quick_search/appstats.js.erb +73 -0
- data/app/assets/javascripts/quick_search/appstats_best_bets.js.erb +905 -0
- data/app/assets/javascripts/quick_search/appstats_clicks_overview.js.erb +719 -0
- data/app/assets/javascripts/quick_search/appstats_general_statistics.js.erb +908 -0
- data/app/assets/javascripts/quick_search/appstats_sessions_details.js.erb +660 -0
- data/app/assets/javascripts/quick_search/appstats_sessions_overview.js.erb +481 -0
- data/app/assets/javascripts/quick_search/appstats_spelling_suggestions.js.erb +902 -0
- data/app/assets/javascripts/quick_search/appstats_top_searches.js.erb +633 -0
- data/app/assets/javascripts/quick_search/event_tracking.js.erb +22 -33
- data/app/assets/javascripts/quick_search/xhr_search.js +7 -3
- data/app/assets/stylesheets/quick_search/appstats.scss +148 -0
- data/app/assets/stylesheets/quick_search/quick_search.scss +0 -16
- data/app/controllers/concerns/quick_search/doi_trap.rb +1 -1
- data/app/controllers/quick_search/appstats_controller.rb +267 -144
- data/app/controllers/quick_search/logging_controller.rb +165 -0
- data/app/controllers/quick_search/search_controller.rb +1 -23
- data/app/controllers/quick_search/typeahead_controller.rb +48 -0
- data/app/models/event.rb +6 -0
- data/app/models/search.rb +7 -0
- data/app/models/session.rb +9 -0
- data/app/searchers/quick_search/best_bets_searcher.rb +1 -1
- data/app/views/layouts/quick_search/_google_analytics.html.erb +4 -9
- data/app/views/layouts/quick_search/_search_form.html.erb +2 -2
- data/app/views/layouts/quick_search/application.html.erb +4 -0
- data/app/views/quick_search/appstats/_date_range.html.erb +14 -13
- data/app/views/quick_search/appstats/_graph_best_bets.html.erb +6 -0
- data/app/views/quick_search/appstats/_graph_clicks_overview.html.erb +12 -0
- data/app/views/quick_search/appstats/_graph_general_statistics.html.erb +19 -0
- data/app/views/quick_search/appstats/_graph_sessions_details.html.erb +19 -0
- data/app/views/quick_search/appstats/_graph_sessions_overview.html.erb +9 -0
- data/app/views/quick_search/appstats/_graph_spelling_suggestions.html.erb +6 -0
- data/app/views/quick_search/appstats/_graph_top_searches.html.erb +6 -0
- data/app/views/quick_search/appstats/_menu.html.erb +16 -3
- data/app/views/quick_search/appstats/_sessions_details_device_filters.html.erb +9 -0
- data/app/views/quick_search/appstats/_sessions_details_location_filters.html.erb +9 -0
- data/app/views/quick_search/appstats/_sessions_overview_graph_filters.html.erb +9 -0
- data/app/views/quick_search/appstats/clicks_overview.html.erb +3 -14
- data/app/views/quick_search/appstats/index.html.erb +3 -23
- data/app/views/quick_search/appstats/sessions_details.html.erb +9 -0
- data/app/views/quick_search/appstats/sessions_overview.html.erb +9 -0
- data/app/views/quick_search/appstats/top_searches.html.erb +3 -32
- data/app/views/quick_search/appstats/top_spot.html.erb +7 -7
- data/app/views/quick_search/search/_module_with_paging.html.erb +1 -1
- data/app/views/quick_search/search/_result_title.html.erb +1 -1
- data/config/quicksearch_config.yml.example +3 -0
- data/config/routes.rb +31 -3
- data/db/migrate/20161201141003_create_sessions.rb +12 -0
- data/db/migrate/20161201142811_change_event_columns.rb +11 -0
- data/db/migrate/20161201143901_add_session_id_to_searches.rb +5 -0
- data/db/migrate/20161212192454_add_date_string_columns.rb +11 -0
- data/db/seeds.rb +83 -7
- data/lib/generators/quick_search/templates/quick_search_typeahead.js +253 -0
- data/lib/generators/quick_search/typeahead_generator.rb +162 -0
- data/lib/quick_search/engine.rb +1 -0
- data/lib/quick_search/version.rb +1 -1
- data/test/dummy/db/schema.rb +25 -6
- data/test/dummy/log/benchmark.log +121 -241
- data/test/dummy/log/development.log +236 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/0b/0bXp2nnjPLNuYQRAMo1RkhNr8RSC8vqZo_7soo0XZTE.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/3p/3pAfj3gt9c9rLmOrDkMvW2r6Swm5YhuGQ1EN3va9LDE.cache +6 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/3r/3r7qg-LCIcD5882b6GKgRTZycpAlhoSNJccVH06yRCQ.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{UA/UAOIipK_ejTAUFI6XxASoweyUiC3t64VfVzV-MfsFD8.cache → 4h/4hLiHzJdffGLMZCeIkbisOdFQSyqs54YkoZpBpGotVk.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{Hr/Hr-kCuPY72nIKAdnsDTz9JqXd_z1WL6FSGMAq4HppZg.cache → 5Y/5Yh8GnSzywqhuMWMloKnL9FnuIxa9rBw6hN2Ecv0urw.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/8h/8hHsBZ0Il3GVKNj0HgTh-s-XpPVkHrcSY0EJ4v548sk.cache +4 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{fI/fInu_Gja7ik39t207tqj4CtlL0akQRbjD2T7qEtd0zE.cache → 8n/8nKAboU_CeVIX30-Q9BtSV5TXdE07WU8hO0OQ0N3KOs.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{dz/dzCaa80YgyvJINTgsyrVhKv9UQ1OC82GfAsA-IP96iQ.cache → Af/Afug8duevWkZFPkOMnZ5z_IbRT6JMZo4jw1QFmLiXXA.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/C2/C24SLDoUbdiHRmPZx5lIZ3XPXmn9FDq7j7gZmbpDtcE.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/CH/CHuDjj47dHGqRD3uN1JNwnlFyjjEKB09KR6WVsLj2Do.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Ci/Cif3NOwhu5i6Qm3iRv2gNJLi_cu8yDVz0Nrq8TZu1tc.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/FQ/FQWd8V-Pek1LBAfW9vzcG-FpWySsPRhMVzH9DlPxLWU.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Fa/FaYEz8CfLTkxVZvnbgKGvJMhhBwNvEgsZgUwaHNRlaQ.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/IA/IALQq6ICbs3VBSRsaUmGuN73Qv5wDfWTP86VmMh_sVs.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/K1/K1jnTrW-_s64kqCA6fnoQ-ID-rDaGX34J-TNPyzDbLs.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Mz/MzXE1Go_GKyu6AvJPhw394BtPM_jVTf0hMwobBi15tk.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Os/OsIhoBDw5N2AWGQ-EMK79tgwPS1AC6Dy_gMSJdOv2Ss.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{B5/B5ptGSZGmgHvoK2UDCAtpMhWxJEKxKFhyL9ACeBfzQk.cache → PC/PCth9nzoRrp5GI3Ut8FxlGpXjH63V7k2p4PRwfyu2sQ.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Pw/Pw3Jv2p7MsQz6pttCZig6VMVQ30sRyIDRS-2nM32yqY.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/RI/RIOgtcuvLAJZl62RvAzqine3jCO3i2qyhswiW2G1bw8.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/R_/R_6WhfFaPUAOAFfFd4bgUonI5aB4Fa6iOMY2J6G3U98.cache +4 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{pj/pjJDYrBipDo6Tr9_-W4UDzfZZ4UPTy9YpXrBQFRXnlo.cache → S3/S3gPdA-Zqya5r6wNhnlwqGV3B3-GDaA8i1fL_jo66AI.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/SS/SS3eYrFE6ghi5G_3POW760Y4BWo5OAaK5QjDrRGXOcs.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{jj/jjdQCKZsewws2eRP7b2AVxRMvseOp8djjlDkB8UOtac.cache → Sq/SqjdxmYsdurpx9xk1ks32WEtf3D9_2BysE7RMxnOlmU.cache} +1 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{os/os0qWv3yDfep0e7vVSGTgfn_n3xc4C4QZgmMzLQn0Ks.cache → Ta/TaJnQ_l0sfSsDOyVoi-S-KZQ6vIwzBhmS6hmNA_n8ug.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{o6/o6ZQbq1aBFuVrE2xLbSbyZH2736sWM3Yj4ky9chT1bI.cache → X_/X_CxFXEdZo2C_in7FopAuAbplHn6W_V0SXyVQAMnTzI.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{sR/sRU-UT8qK_38kkiFDXKBPogO1oOZuSvJFH-pgqPL_Ms.cache → Yh/YhRWll6bzgYE6Dmc5fVKbHyv5om4y3-27UrWeUbQcGk.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{OD/ODHapE0li3WI1laVJoksoI5nmQd-8dDJXq_VxS4XefE.cache → _u/_uiA10I5HyJR94Tbc4a06CzdrQCAXX7Iz86WS5PKDQo.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/ai/ainCVF4dxXf85KoOLUSHvMMGg0qBNc7Vg-s-9v2ENJI.cache +4 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/cQ/cQqt0ri9GFvVnIs1pJUugIJs5tDuGQMMntW5CwpK6YY.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{1n/1nStVaWIQEwAlksUpB1fInwq6v3TikPvuRaSwwCnCLQ.cache → e0/e0Y2Gz8ftaW2O0RjoN2iWguitI-JQZckScAZqlQYkng.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/iH/iHnlNNY3rfdbVqt8LZBfcDQYONR8dA8LmxA9fv5paro.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{F0/F0oWBwLHpGFYNNtnGav-dJFH8rMDpNA5L8_aWm5Wq34.cache → ir/irxgIDPIY6Wz6fHWYp1dIrqAT1_8oa6-oiwqbFE_e-w.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/kn/knUtyVrg7nMIPldZa4MHhe7cC9eMchLZS4mc5HDWgDY.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/ks/ks4DoIuUV6oUzoiXoPh7sW1k5ExNoUkR7ICjwuRnAT0.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/m3/m375QQakjXd_2Trpvdot-i90nmeCCh0HWtzS66ZvxUU.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/rQ/rQ_FgYkT2hdw9A6XmJKGgVb2dMtCrWWHwtS0X1_kdl0.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{pw/pwiIhcWPwTuy5Miz5zqOQGYFx-K3TbaCVQ6ls7EBkk0.cache → rU/rUqFWwHqrT5jeIFCEezOPsMMZiAU7ri6RKBGS2ezAfg.cache} +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/{pO/pO8hHVwybv1ZJBdPa2YC6bjqmk020OYFl85TYxtmrWQ.cache → rq/rqf5-J6k_zLc9ZlUgMj28nviKtRsko3-iEp_m_uNeY0.cache} +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- metadata +143 -119
- data/app/assets/javascripts/quick_search/appstats.js +0 -8
- data/app/views/quick_search/appstats/_click_count_table.html.erb +0 -23
- data/app/views/quick_search/appstats/_top_spot_report_table.html.erb +0 -33
- data/app/views/quick_search/appstats/module_click_detail.html.erb +0 -32
- data/app/views/quick_search/appstats/top_spot_detail.html.erb +0 -35
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/0_/0_w_ydOOEFQOoETxJa-IxKppQocaiDwM5V08djCPYso.cache +0 -3
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/3m/3mWfBeEpm_bJvaPQZYvnnAVtE_2helOWh_Tv-cEflrk.cache +0 -5
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/4s/4sg52OgD5l4qx4GnojCVxg2itqwqb-KRanEdmdMus3Y.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/5U/5UPnsccuZFLk9OqbmFMvB55mbvGptApx9Z55HWjEN90.cache +0 -3
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/9a/9acKNgyVD8_WK401tMy6Ce98g-LS4VpwFvGQdzCV9mI.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Ba/BaymviI8cwAJQMOIzWCf6Lt-PM2sNFTvY9hQKsfDNNw.cache +0 -3
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Dd/DdFHg3C_voetfvKGKX7t8PGzZlkO45-huHNtaou5rxg.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Fp/FprCQeeZguht46WNSpA3Y3JHKehovFXrj63nDrvvwFE.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/GJ/GJrfWiarIi0Dt6sdKVMfUF1cdZ6NbqZxXHhzNbFtpBw.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Gp/GpoziOxxB-hisco0rl7Nnsnw0UWud7R9Yge2Q3o1eN4.cache +0 -2
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/It/It5VkJv-53-LICLKZWWyp2WcbD86DGotAL0MY3hhfpg.cache +0 -6
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/O6/O6dw148Xya7NYlsRFMEzJyvRcySM6iOL1jRUpdQo9B8.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Oj/OjMkKTW7OkthjJAQHDID8GV30IZC9F0dJ1zGiFame9U.cache +0 -4
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/SU/SUtZ_ltPqy9A9A-_zqNPhNPvjxCOnrdqXHSZv5S9mOk.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Tv/TvZgmm69VEMHUJQIBoij61oXMkgzp6whVo7l8mJ9qpw.cache +0 -3
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/YJ/YJZdK01T1KTS84Mb6p_2TasmJNZeGZc4FMtBPCKGr_M.cache +0 -3
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/ah/ahhhd3ysbQmVDNzq2L_rb4t6rqqdnqeY6ujztBoZDA8.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/gv/gvZKkbgiExbcQrT4eo0HB-j4JoDRAxdmDjKRxeEdK0Y.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/hj/hj_r-O1QjHJIPcZVnlB_FGPdIRAi9NkxpT2oXDRIsGE.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/o2/o2V1ahk8vFR6sXWNh5BB7w6MBj04zCyX-o1xFZONydE.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/tE/tEO71oPPFXN_2c4p39nU0OiF9ARg13fjdQlnPqAx1KY.cache +0 -4
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/tW/tWFRsAv4WWu2ch-x3AJZd3KOvBXG3MwH6Q6Xn-tFBtw.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/x6/x6W754ylumshr3wBUHHmcgyWs8UR3Od7FaI6tqvjKmk.cache +0 -1
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/yy/yyJ1P7RQciOiW8TgQiu1RMDdbWd4rqxd2BGU9HkY3L8.cache +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
module QuickSearch
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# This class handles QuickSearch's internal logging of queries, events (clicks & serves), and sessions
|
|
5
|
+
|
|
6
|
+
class LoggingController < ApplicationController
|
|
7
|
+
|
|
8
|
+
include QuickSearch::OnCampus
|
|
9
|
+
|
|
10
|
+
before_action :handle_session
|
|
11
|
+
protect_from_forgery except: :log_event
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
# Logs a search to the database
|
|
15
|
+
#
|
|
16
|
+
# This is an API endpoint for logging a search. It requires that at least a search query and a page are
|
|
17
|
+
# present in the query parameters. It returns a 200 OK HTTP status if the request was successful, or
|
|
18
|
+
# an 400 BAD REQUEST HTTP status if any parameters are missing.
|
|
19
|
+
|
|
20
|
+
def log_search
|
|
21
|
+
if params[:query].present? && params[:page].present?
|
|
22
|
+
@session.searches.create(query: params[:query], page: params[:page])
|
|
23
|
+
head :ok
|
|
24
|
+
else
|
|
25
|
+
head :bad_request
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Logs an event to the database. Typically, these can be clicks or serves.
|
|
31
|
+
#
|
|
32
|
+
# This is an API endpoint for logging an event. It requires that at least a TODO are
|
|
33
|
+
# present in the query parameters. It returns a 200 OK HTTP status if the request was successful, or
|
|
34
|
+
# an 400 BAD REQUEST HTTP status if any parameters are missing. This endpoint supports JSONP requests.
|
|
35
|
+
|
|
36
|
+
def log_event
|
|
37
|
+
if params[:category].present? && params[:event_action].present? && params[:label].present?
|
|
38
|
+
# if an action isn't passed in, assume that it is a click
|
|
39
|
+
action = params.fetch(:action_type, 'click')
|
|
40
|
+
|
|
41
|
+
# create a new event on the current session
|
|
42
|
+
@session.events.create(category: params[:category], item: params[:event_action], query: params[:label][0..250], action: action)
|
|
43
|
+
|
|
44
|
+
if params[:ga].present? and params[:ga]
|
|
45
|
+
send_event_to_ga(params[:category], params[:event_action], params[:label])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# check whether this is a jsonp request
|
|
49
|
+
if params[:callback].present?
|
|
50
|
+
render :json => { 'response': 'success' }, :callback => params[:callback]
|
|
51
|
+
else
|
|
52
|
+
render :json => { 'response': 'success' }
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
head :bad_request
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# Logs an event to Google Analytics using the Measurement Protocol API
|
|
64
|
+
# https://developers.google.com/analytics/devguides/collection/protocol/v1/
|
|
65
|
+
|
|
66
|
+
def send_event_to_ga(category, action, label)
|
|
67
|
+
# google_analytics_client_id is a UUID that identifies a particular client to the GA Measurement Protocol API
|
|
68
|
+
if QuickSearch::Engine::APP_CONFIG['google_analytics_tracking_id'].blank? or QuickSearch::Engine::APP_CONFIG['google_analytics_client_id'].blank?
|
|
69
|
+
return false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Pass along the client user agent and IP address so it is associated
|
|
73
|
+
# with the event in Google Analytics
|
|
74
|
+
params = {
|
|
75
|
+
v: 1,
|
|
76
|
+
t: 'event',
|
|
77
|
+
tid: QuickSearch::Engine::APP_CONFIG['google_analytics_tracking_id'],
|
|
78
|
+
cid: QuickSearch::Engine::APP_CONFIG['google_analytics_client_id'],
|
|
79
|
+
ec: category,
|
|
80
|
+
ea: action,
|
|
81
|
+
el: label,
|
|
82
|
+
uip: request.remote_ip,
|
|
83
|
+
ua: request.user_agent,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
client = HTTPClient.new
|
|
87
|
+
url = "https://www.google-analytics.com/collect?" + params.to_query
|
|
88
|
+
|
|
89
|
+
# The measurement protocol API does not validate responses.
|
|
90
|
+
# The best way to test this is testing the query string against:
|
|
91
|
+
# https://ga-dev-tools.appspot.com/hit-builder/
|
|
92
|
+
|
|
93
|
+
client.post(url)
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
##
|
|
98
|
+
# Handles creating/updating a session on every request
|
|
99
|
+
|
|
100
|
+
def handle_session
|
|
101
|
+
if is_existing_session?
|
|
102
|
+
update_session
|
|
103
|
+
else
|
|
104
|
+
new_session
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# Returns true if current request has an existing session, false otherwise
|
|
110
|
+
|
|
111
|
+
def is_existing_session?
|
|
112
|
+
cookies.has_key? :session_id and Session.find_by(session_uuid: cookies[:session_id])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
##
|
|
116
|
+
# Returns true if current request was from a mobile device
|
|
117
|
+
#
|
|
118
|
+
# Uses User-Agent from request to make the determination, which may not be all-encompassing
|
|
119
|
+
# but works for most modern devices/browsers (iOS, Android). Looks for the string "Mobi" within
|
|
120
|
+
# the user-agent, which normally contains either Mobi or Mobile if the request was from a mobile browser
|
|
121
|
+
|
|
122
|
+
def is_mobile?
|
|
123
|
+
# TODO: better test for mobile?
|
|
124
|
+
# Recommended here as simple test: https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
|
|
125
|
+
request.user_agent.include? "Mobi"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
##
|
|
129
|
+
# Creates a new session, and logs it in the database
|
|
130
|
+
#
|
|
131
|
+
# A session is tracked by a UUID that is stored in a cookie, and has a 5 minute expiry time.
|
|
132
|
+
# Sessions are stored in the database with the time they were initiated, their expiry time (or end time),
|
|
133
|
+
# whether the request originated from a campus IP address, and whether the request originated from a mobile device
|
|
134
|
+
|
|
135
|
+
def new_session
|
|
136
|
+
on_campus = on_campus?(request.remote_ip)
|
|
137
|
+
is_mobile = is_mobile?
|
|
138
|
+
session_expiry = 5.minutes.from_now
|
|
139
|
+
session_uuid = SecureRandom.uuid
|
|
140
|
+
|
|
141
|
+
# create session in db
|
|
142
|
+
@session = Session.create(session_uuid: session_uuid, expiry: session_expiry, on_campus: on_campus, is_mobile: is_mobile)
|
|
143
|
+
# set cookie
|
|
144
|
+
cookies[:session_id] = { :value => session_uuid, :expires => session_expiry }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
##
|
|
148
|
+
# Updates a session's expiration time on cookie and in database
|
|
149
|
+
#
|
|
150
|
+
# When a request is made with a non-expired session, the expiration time is updated to 5 minutes from the current time.
|
|
151
|
+
# This update is reflected in the cookie as well as in the database entry for the session.
|
|
152
|
+
|
|
153
|
+
def update_session
|
|
154
|
+
# update session expiry in the database
|
|
155
|
+
session_id = cookies[:session_id]
|
|
156
|
+
@session = Session.find_by session_uuid: session_id
|
|
157
|
+
@session.expiry = 5.minutes.from_now
|
|
158
|
+
@session.save
|
|
159
|
+
|
|
160
|
+
# update session expiry on cookie
|
|
161
|
+
cookies[:session_id] = { :value => session_id, :expires => @session.expiry }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -12,7 +12,6 @@ module QuickSearch
|
|
|
12
12
|
|
|
13
13
|
before_action :doi_trap, :log_query
|
|
14
14
|
after_action :realtime_message, only: [:index]
|
|
15
|
-
protect_from_forgery except: :log_event
|
|
16
15
|
|
|
17
16
|
def index
|
|
18
17
|
loaded_searches
|
|
@@ -114,28 +113,6 @@ module QuickSearch
|
|
|
114
113
|
end
|
|
115
114
|
end
|
|
116
115
|
|
|
117
|
-
def log_search
|
|
118
|
-
if params[:query].present? && params[:page].present?
|
|
119
|
-
Search.create(query: params[:query], page: params[:page])
|
|
120
|
-
head :ok
|
|
121
|
-
else
|
|
122
|
-
head :bad_request
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def log_event
|
|
127
|
-
if params[:category].present? && params[:event_action].present? && params[:label].present?
|
|
128
|
-
Event.create(category: params[:category], action: params[:event_action], label: params[:label][0..250])
|
|
129
|
-
if params[:callback].present?
|
|
130
|
-
head :ok, content_type: 'text/javascript'
|
|
131
|
-
else
|
|
132
|
-
head :ok
|
|
133
|
-
end
|
|
134
|
-
else
|
|
135
|
-
head :bad_request
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
116
|
private
|
|
140
117
|
|
|
141
118
|
def http_search(endpoint = 'defaults', page_to_render = :index)
|
|
@@ -238,6 +215,7 @@ module QuickSearch
|
|
|
238
215
|
result
|
|
239
216
|
end
|
|
240
217
|
|
|
218
|
+
# TODO: move this --- is this necessary?
|
|
241
219
|
def log_query
|
|
242
220
|
if search_in_params?
|
|
243
221
|
@log_query = filter_query(params_q_scrubbed)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module QuickSearch
|
|
2
|
+
class TypeaheadController < ApplicationController
|
|
3
|
+
protect_from_forgery except: :typeahead
|
|
4
|
+
|
|
5
|
+
# This method should return a list of search suggestions for a given searcher
|
|
6
|
+
# It should return errors if there is no param called 'searcher', if the searcher does not exist
|
|
7
|
+
# or if the searcher doesn't implement the 'typeahead' method
|
|
8
|
+
# Otherwise, it should return the result of calling the 'typeahead' method as JSON
|
|
9
|
+
|
|
10
|
+
def typeahead
|
|
11
|
+
# :searcher param expected to be name of a searcher with underscores, ie: ematrix_journal
|
|
12
|
+
searcher = params[:searcher]
|
|
13
|
+
query = params[:q] or ''
|
|
14
|
+
total = params[:total] or 3
|
|
15
|
+
|
|
16
|
+
if searcher.blank?
|
|
17
|
+
logger.error "Typeahead request: no searcher param provided"
|
|
18
|
+
head :bad_request
|
|
19
|
+
return nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
searcher_string = "QuickSearch::#{searcher.camelize}Searcher"
|
|
23
|
+
|
|
24
|
+
begin
|
|
25
|
+
klass = searcher_string.constantize
|
|
26
|
+
rescue NameError
|
|
27
|
+
logger.error "Typeahead request: searcher #{searcher} does not exist"
|
|
28
|
+
head :bad_request
|
|
29
|
+
return nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if klass.method_defined? :typeahead
|
|
33
|
+
typeahead_result = klass.new(HTTPClient.new, query, total).typeahead
|
|
34
|
+
|
|
35
|
+
if params.has_key? 'callback'
|
|
36
|
+
render json: typeahead_result, callback: params['callback']
|
|
37
|
+
else
|
|
38
|
+
render json: typeahead_result
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
logger.error "Typeahead request: searcher #{searcher} has no typeahead method"
|
|
42
|
+
head :bad_request
|
|
43
|
+
return nil
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
data/app/models/event.rb
CHANGED
data/app/models/search.rb
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
<% if Rails.env == 'production' or Rails.env == 'staging' %>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
_gaq.push(['_setAccount', "<%= QuickSearch::Engine::APP_CONFIG['google_analytics_tracking_id'] %>"]);
|
|
6
|
-
_gaq.push(['_trackPageview']);
|
|
3
|
+
// Universal analytics
|
|
4
|
+
(function(i,s,o,g,r,a,m){i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,"script","//www.google-analytics.com/analytics.js","ga");
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
|
11
|
-
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
|
12
|
-
})();
|
|
6
|
+
ga("create", "<%= QuickSearch::Engine::APP_CONFIG['google_analytics_tracking_id'] %>", "auto");
|
|
7
|
+
ga("send", "pageview");
|
|
13
8
|
|
|
14
9
|
</script>
|
|
15
10
|
<% end %>
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<div class="row collapse">
|
|
5
5
|
<div class="small-10 columns">
|
|
6
6
|
<% if @query %>
|
|
7
|
-
<input type="search" name="q" id="params-q" class="
|
|
7
|
+
<input type="search" name="q" id="params-q" class="search-query" value="<%= @query %>" />
|
|
8
8
|
<% else %>
|
|
9
|
-
<input type="search" name="q" id="params-q" class="
|
|
9
|
+
<input type="search" name="q" id="params-q" class="search-query" placeholder="<%= @search_form_placeholder %>" />
|
|
10
10
|
<% end %>
|
|
11
11
|
</div>
|
|
12
12
|
<div class="small-2 columns">
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
<%= javascript_include_tag 'application' %>
|
|
13
13
|
<%= render '/layouts/quick_search/head' %>
|
|
14
14
|
<%= render '/layouts/quick_search/google_analytics' %>
|
|
15
|
+
|
|
16
|
+
<!-- datepicker (temp) -->
|
|
17
|
+
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
|
18
|
+
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
|
15
19
|
</head>
|
|
16
20
|
<body class="<%= body_class %>" data-quicksearch-ga-query="<%= strip_tags(@log_query) %>">
|
|
17
21
|
<div class="off-canvas-wrap">
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
<% title('Foo') %>
|
|
2
|
-
<div class="
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</
|
|
2
|
+
<div class="medium-2 columns" style="position: fixed; right: 0;">
|
|
3
|
+
<div style="">
|
|
4
|
+
<label for="from" class="inputLabel"> From: </label>
|
|
5
|
+
<input id="from" type="text" name="from">
|
|
6
|
+
<label for="to" class="inputLabel"> To: </label>
|
|
7
|
+
<input id="to" type="text" name="to">
|
|
8
|
+
<p id="datePickerText"> Days in Sample: </p>
|
|
9
|
+
<p id="numDays"><%= "#{@days_in_sample}" %></p>
|
|
10
|
+
<% if @page_title=='spelling-suggestion'||@page_title=='best-bets-regular'||@page_title=='Top Searches' %>
|
|
11
|
+
<label for="num" class="inputLabel"> # Results: </label>
|
|
12
|
+
<input id="num" type="number" name="num" min="0" max="200">
|
|
13
|
+
<% end %>
|
|
14
|
+
<button id="dateButton" type="button"> Update </button>
|
|
15
|
+
<div>
|
|
15
16
|
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<h1><%= @page_title %></h1>
|
|
2
|
+
<h2>Frequency of Modules Clicked</h2>
|
|
3
|
+
<div class="graphGroup">
|
|
4
|
+
<div class="iconContainer" id="moduleIconContainer"></div>
|
|
5
|
+
<svg id="graph_module_clicks" width="840" height="240"></svg>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<h2>Frequency of Result-Type Guides Clicked</h2>
|
|
9
|
+
<div class="graphGroup">
|
|
10
|
+
<div class="iconContainer" id="resultIconContainer"></div>
|
|
11
|
+
<svg id="graph_result_clicks" width="840" height="240"></svg>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<h1><%= @page_title %></h1>
|
|
2
|
+
<div class="graphGroup">
|
|
3
|
+
<div class="iconContainer" id="tableIconContainer"></div>
|
|
4
|
+
<svg class="graph" id="graph_general_table" width="840" height="473"></svg>
|
|
5
|
+
</svg>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="graphGroup">
|
|
9
|
+
<div class="iconContainer" id="graphIconContainer"></div>
|
|
10
|
+
<svg class="graph" id="graph_general_statistics" width="840" height="473">
|
|
11
|
+
</svg>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div style="display: inline-block; width: 1000px;">
|
|
15
|
+
<svg class="graph_mini" id="mini0" width="280" height="158" onmouseover="lighten()" onmouseout="darken()"></svg>
|
|
16
|
+
<svg class="graph_mini" id="mini1" width="280" height="158" onmouseover="lighten()" onmouseout="darken()"></svg>
|
|
17
|
+
<svg class="graph_mini" id="mini2" width="280" height="158" onmouseover="lighten()" onmouseout="darken()"></svg>
|
|
18
|
+
<div class="clearfix"></div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<h1><%= @page_title %></h1>
|
|
2
|
+
<h2> Sessions by Location </h2>
|
|
3
|
+
<div class="graphGroup">
|
|
4
|
+
<div class="iconContainer" id="locationIconContainer"></div>
|
|
5
|
+
<div style="float: left; display: inline-block;">
|
|
6
|
+
<svg id="graph_sessions_location" width="840" height="473"></svg>
|
|
7
|
+
</div>
|
|
8
|
+
<%= render partial: 'sessions_details_location_filters' %>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="clearfix"></div>
|
|
12
|
+
<h2> Sessions by Access Device </h2>
|
|
13
|
+
<div class="graphGroup">
|
|
14
|
+
<div class="iconContainer" id="deviceIconContainer"></div>
|
|
15
|
+
<div style="float: left; display: inline-block;">
|
|
16
|
+
<svg id="graph_sessions_device" width="840" height="473"></svg>
|
|
17
|
+
</div>
|
|
18
|
+
<%= render partial: 'sessions_details_device_filters' %>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<h1><%= @page_title %></h1>
|
|
2
|
+
<h2>Sessions Overview</h2>
|
|
3
|
+
<div class="graphGroup">
|
|
4
|
+
<div class="iconContainer" id="sessIconContainer"></div>
|
|
5
|
+
<div style="float: left; display: inline-block;">
|
|
6
|
+
<svg id="graph_sessions_overview" width="840" height="473"></svg>
|
|
7
|
+
</div>
|
|
8
|
+
<%= render partial: 'sessions_overview_graph_filters' %>
|
|
9
|
+
</div>
|