quick_search-core 0.0.1.test

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +168 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/images/quick_search/askus.gif +0 -0
  6. data/app/assets/images/quick_search/favicon.ico +0 -0
  7. data/app/assets/images/quick_search/greybox.png +0 -0
  8. data/app/assets/images/quick_search/homescreen-icon-64x64.png +0 -0
  9. data/app/assets/images/quick_search/ncsu-library-logo-white.png +0 -0
  10. data/app/assets/javascripts/quick_search/appstats.js +8 -0
  11. data/app/assets/javascripts/quick_search/event_tracking.js.erb +257 -0
  12. data/app/assets/javascripts/quick_search/google_analytics.js.erb +8 -0
  13. data/app/assets/javascripts/quick_search/opensearch.js +2 -0
  14. data/app/assets/javascripts/quick_search/pages.js.coffee +3 -0
  15. data/app/assets/javascripts/quick_search/quicksearch.js +33 -0
  16. data/app/assets/javascripts/quick_search/realtime.js.erb +12 -0
  17. data/app/assets/javascripts/quick_search/search.js +3 -0
  18. data/app/assets/javascripts/quick_search/xhr_search.js +82 -0
  19. data/app/assets/javascripts/quick_search.js.erb +20 -0
  20. data/app/assets/stylesheets/foundation_and_overrides.scss +1327 -0
  21. data/app/assets/stylesheets/quick_search/appstats.css.scss +7 -0
  22. data/app/assets/stylesheets/quick_search/opensearch.css.scss +3 -0
  23. data/app/assets/stylesheets/quick_search/pages.css.scss +3 -0
  24. data/app/assets/stylesheets/quick_search/quick_search.css.scss +248 -0
  25. data/app/assets/stylesheets/quick_search/search.css.scss +3 -0
  26. data/app/assets/stylesheets/quick_search.css.scss.erb +7 -0
  27. data/app/controllers/concerns/quick_search/auth.rb +22 -0
  28. data/app/controllers/concerns/quick_search/doi_trap.rb +43 -0
  29. data/app/controllers/concerns/quick_search/encode_utf8.rb +12 -0
  30. data/app/controllers/concerns/quick_search/on_campus.rb +43 -0
  31. data/app/controllers/concerns/quick_search/query_filter.rb +25 -0
  32. data/app/controllers/concerns/quick_search/query_parser.rb +54 -0
  33. data/app/controllers/concerns/quick_search/searcher_concern.rb +101 -0
  34. data/app/controllers/concerns/quick_search/searcher_config.rb +16 -0
  35. data/app/controllers/quick_search/application_controller.rb +7 -0
  36. data/app/controllers/quick_search/appstats_controller.rb +210 -0
  37. data/app/controllers/quick_search/opensearch_controller.rb +9 -0
  38. data/app/controllers/quick_search/pages_controller.rb +18 -0
  39. data/app/controllers/quick_search/search_controller.rb +250 -0
  40. data/app/helpers/quick_search/application_helper.rb +68 -0
  41. data/app/helpers/quick_search/appstats_helper.rb +2 -0
  42. data/app/helpers/quick_search/opensearch_helper.rb +2 -0
  43. data/app/helpers/quick_search/pages_helper.rb +2 -0
  44. data/app/helpers/quick_search/search_helper.rb +2 -0
  45. data/app/models/event.rb +2 -0
  46. data/app/models/search.rb +11 -0
  47. data/app/searchers/quick_search/best_bets_searcher.rb +102 -0
  48. data/app/searchers/quick_search/searcher.rb +43 -0
  49. data/app/views/layouts/quick_search/_best_bets.html.erb +18 -0
  50. data/app/views/layouts/quick_search/_common_searches.html.erb +15 -0
  51. data/app/views/layouts/quick_search/_found_types.html.erb +13 -0
  52. data/app/views/layouts/quick_search/_google_analytics.html.erb +24 -0
  53. data/app/views/layouts/quick_search/_more_options.html.erb +10 -0
  54. data/app/views/layouts/quick_search/_page_title.html.erb +8 -0
  55. data/app/views/layouts/quick_search/_related_topics.html.erb +8 -0
  56. data/app/views/layouts/quick_search/_search_form.html.erb +19 -0
  57. data/app/views/layouts/quick_search/_spelling_suggestion.html.erb +13 -0
  58. data/app/views/layouts/quick_search/_topic_snippet.html.erb +5 -0
  59. data/app/views/layouts/quick_search/application.html.erb +40 -0
  60. data/app/views/quick_search/appstats/_click_count_table.html.erb +23 -0
  61. data/app/views/quick_search/appstats/_datatables.html.erb +12 -0
  62. data/app/views/quick_search/appstats/_date_range.html.erb +15 -0
  63. data/app/views/quick_search/appstats/_menu.html.erb +16 -0
  64. data/app/views/quick_search/appstats/_top_spot_report_table.html.erb +33 -0
  65. data/app/views/quick_search/appstats/clicks_overview.html.erb +20 -0
  66. data/app/views/quick_search/appstats/index.html.erb +31 -0
  67. data/app/views/quick_search/appstats/module_click_detail.html.erb +32 -0
  68. data/app/views/quick_search/appstats/realtime.html.erb +15 -0
  69. data/app/views/quick_search/appstats/top_searches.html.erb +38 -0
  70. data/app/views/quick_search/appstats/top_spot.html.erb +14 -0
  71. data/app/views/quick_search/appstats/top_spot_detail.html.erb +35 -0
  72. data/app/views/quick_search/kaminari/_first_page.html.erb +12 -0
  73. data/app/views/quick_search/kaminari/_gap.html.erb +11 -0
  74. data/app/views/quick_search/kaminari/_last_page.html.erb +12 -0
  75. data/app/views/quick_search/kaminari/_next_page.html.erb +12 -0
  76. data/app/views/quick_search/kaminari/_page.html.erb +13 -0
  77. data/app/views/quick_search/kaminari/_paginator.html.erb +25 -0
  78. data/app/views/quick_search/kaminari/_prev_page.html.erb +11 -0
  79. data/app/views/quick_search/opensearch/opensearch.xml.builder +11 -0
  80. data/app/views/quick_search/pages/about.html.erb +9 -0
  81. data/app/views/quick_search/pages/home.html.erb +6 -0
  82. data/app/views/quick_search/pages/realtime.html.erb +15 -0
  83. data/app/views/quick_search/search/_citation.html.erb +18 -0
  84. data/app/views/quick_search/search/_module.html.erb +37 -0
  85. data/app/views/quick_search/search/_module_with_paging.html.erb +14 -0
  86. data/app/views/quick_search/search/_no_results.html.erb +11 -0
  87. data/app/views/quick_search/search/_result.html.erb +17 -0
  88. data/app/views/quick_search/search/_result_details.html.erb +45 -0
  89. data/app/views/quick_search/search/_result_no_thumbnail.html.erb +3 -0
  90. data/app/views/quick_search/search/_result_title.html.erb +4 -0
  91. data/app/views/quick_search/search/_result_with_thumbnail.html.erb +21 -0
  92. data/app/views/quick_search/search/_search_error.html.erb +9 -0
  93. data/app/views/quick_search/search/_see_all.html.erb +11 -0
  94. data/app/views/quick_search/search/_xhr_response.html.erb +15 -0
  95. data/app/views/quick_search/search/_xhr_response.json.erb +14 -0
  96. data/app/views/quick_search/search/_xhr_response_with_paging.html.erb +1 -0
  97. data/app/views/quick_search/search/index.html.erb +100 -0
  98. data/config/application.rb +25 -0
  99. data/config/best_bets.yml +86 -0
  100. data/config/boot.rb +4 -0
  101. data/config/database.yml.example +31 -0
  102. data/config/locales/en.yml +30 -0
  103. data/config/quicksearch_config.yml.example +69 -0
  104. data/config/routes.rb +81 -0
  105. data/db/migrate/20140130202859_create_events.rb +11 -0
  106. data/db/migrate/20140130202944_create_searches.rb +9 -0
  107. data/db/migrate/20140225145441_add_page_to_searches.rb +5 -0
  108. data/db/schema.rb +31 -0
  109. data/db/seeds.rb +7 -0
  110. data/lib/benchmark_logger.rb +2 -0
  111. data/lib/capistrano/tasks/access_check.cap +10 -0
  112. data/lib/generators/quick_search/install_generator.rb +49 -0
  113. data/lib/generators/quick_search/templates/kaminari.rb +5 -0
  114. data/lib/generators/quick_search/templates/post_install.txt +8 -0
  115. data/lib/generators/quick_search/templates/quick_search_config.yml +58 -0
  116. data/lib/quick_search/engine.rb +16 -0
  117. data/lib/quick_search/version.rb +3 -0
  118. data/lib/quick_search.rb +12 -0
  119. data/lib/tasks/best_bets.rake +29 -0
  120. data/test/controllers/appstats_controller_test.rb +7 -0
  121. data/test/controllers/concerns/doi_trap_test.rb +19 -0
  122. data/test/controllers/concerns/on_campus_test.rb +19 -0
  123. data/test/controllers/concerns/query_parser_test.rb +47 -0
  124. data/test/controllers/opensearch_controller_test.rb +13 -0
  125. data/test/controllers/pages_controller_test.rb +7 -0
  126. data/test/controllers/search_controller_test.rb +15 -0
  127. data/test/fixtures/events.yml +11 -0
  128. data/test/fixtures/searches.yml +7 -0
  129. data/test/helpers/appstats_helper_test.rb +4 -0
  130. data/test/helpers/opensearch_helper_test.rb +4 -0
  131. data/test/helpers/pages_helper_test.rb +4 -0
  132. data/test/helpers/search_helper_test.rb +4 -0
  133. data/test/integration/catalog_search_test.rb +8 -0
  134. data/test/integration/google_analytics_test.rb +23 -0
  135. data/test/integration/landing_page_test.rb +8 -0
  136. data/test/integration/searching_test.rb +40 -0
  137. data/test/integration/smart_subjects_test.rb +8 -0
  138. data/test/integration/website_search_test.rb +8 -0
  139. data/test/models/event_test.rb +7 -0
  140. data/test/models/search_test.rb +7 -0
  141. data/test/searcher/best_bets_test.rb +35 -0
  142. data/test/searcher/catalog_test.rb +35 -0
  143. data/test/searcher/ematrix_database_test.rb +27 -0
  144. data/test/searcher/ematrix_journal_test.rb +27 -0
  145. data/test/searcher/faq_test.rb +27 -0
  146. data/test/searcher/smart_subjects_test.rb +27 -0
  147. data/test/searcher/summon_test.rb +13 -0
  148. data/test/searcher/website_test.rb +34 -0
  149. data/test/support/vcr_test.rb +40 -0
  150. data/test/test_helper.rb +42 -0
  151. metadata +366 -0
@@ -0,0 +1,210 @@
1
+ module QuickSearch
2
+ class AppstatsController < ApplicationController
3
+ include Auth
4
+
5
+ before_action :auth, :start_date, :end_date, :days_in_sample
6
+
7
+ def index
8
+ @page_title = 'Search Statistics'
9
+ search_click_ratio
10
+ end
11
+
12
+ def clicks_overview
13
+ @page_title = 'Clicks Overview'
14
+ module_clicks
15
+ result_types_clicks
16
+ typeahead_clicks
17
+ end
18
+
19
+ def top_searches
20
+ @page_title = 'Top Searches'
21
+ searches_count
22
+ searches = Search.where(page: 'index').where(date_range)
23
+ queries = []
24
+ searches.each do |search|
25
+ queries << search["query"].downcase
26
+ end
27
+ @query_counts = Hash.new(0)
28
+ queries.each do |query|
29
+ @query_counts[query] += 1
30
+ end
31
+ @query_counts = @query_counts.sort_by {|k,v| v}.reverse[0..199]
32
+ end
33
+
34
+ def top_spot
35
+ @page_title = params[:ga_top_spot_module]
36
+ top_spot_reporting(params[:ga_top_spot_module])
37
+ end
38
+
39
+ def detail
40
+ start_date
41
+ end_date
42
+ @category = params[:ga_category]
43
+ if params[:ga_scope] == 'action'
44
+ @action = params[:ga_action]
45
+ @page_title = "#{@category} (#{URI.decode(@action)})"
46
+ top_spot_detail(@category, @action)
47
+ render "top_spot_detail"
48
+ elsif params[:ga_scope] == 'category'
49
+ @page_title = "#{@category}"
50
+ module_click_detail(@category)
51
+ render "module_click_detail"
52
+ end
53
+ end
54
+
55
+ def realtime
56
+ end
57
+
58
+ private
59
+
60
+ def module_clicks
61
+ @module_clicks = Event.where(date_range).where(excluded_categories).group(:category).order("count_category DESC").count(:category)
62
+ @module_click_total = calculate_total_clicks(@module_clicks)
63
+ end
64
+
65
+ def typeahead_clicks
66
+ searches_count
67
+ @typeahead_clicks = Event.where(date_range).where("category LIKE 'typeahead-%'").group(:category).order("count_category DESC").count(:category)
68
+ @typeahead_clicks_total = calculate_total_clicks(@typeahead_clicks)
69
+ end
70
+
71
+ def result_types_clicks
72
+ @result_types_clicks = Event.where(date_range).where(:category => "result-types").group(:action).order("count_action DESC").count(:action)
73
+ @result_types_click_total = calculate_total_clicks(@result_types_clicks)
74
+ end
75
+
76
+ def calculate_total_clicks(clicks)
77
+ click_total = 0
78
+ clicks.each do |module_name, click_count|
79
+ click_total += click_count
80
+ end
81
+
82
+ click_total
83
+ end
84
+
85
+ def searches_count
86
+ @searches_count = Search.where(page: 'index').where(date_range).count
87
+ end
88
+
89
+ def search_click_ratio
90
+ searches_count
91
+ @clicks_count = Event.where(excluded_categories).where(date_range).count
92
+ @click_serve_ratio = (@clicks_count / @searches_count.to_f)*100
93
+ end
94
+
95
+ def top_spot_reporting(top_spot_module)
96
+ reports = {"best-bets-regular" => {:category_title => "Best Bets Regular"},
97
+ "best-bets-algorithmic-journal" => {:category_title => "Journal Algorithmic Best Bets"},
98
+ "best-bets-algorithmic-database" => {:category_title => "Database Algorithmic Best Bets"},
99
+ "best-bets-course-tools" => {:category_title => "Course Tools Best Bets"},
100
+ "spelling-suggestion" => {:category_title => "Spelling Suggestions"}
101
+ }
102
+ @top_spot_reporting = []
103
+ top_spot_report = base_query(top_spot_module)
104
+ @top_spot_reporting << {:category => top_spot_module, :category_title => reports[top_spot_module][:category_title], :report => top_spot_report}
105
+ # reports.each do |key, report|
106
+ # top_spot_report = base_query(key)
107
+ # @top_spot_reporting << {:category => key, :category_title => report[:category_title], :report => top_spot_report}
108
+ # end
109
+ end
110
+
111
+ def module_click_detail(category)
112
+ modules_clicks = Event.where(:category => "#{category}").where(date_range).group(:action).order('count_category DESC').count(:category)
113
+ total_clicks = modules_clicks.values.sum
114
+ @modules_clicks_report = []
115
+ modules_clicks.each do |module_clicks|
116
+ @modules_clicks_report << {:module => module_clicks[0], :clicks => module_clicks[1], :percent => (module_clicks[1]/total_clicks.to_f)*100}
117
+ end
118
+ end
119
+
120
+ def top_spot_detail(category, action)
121
+ # Use a join here?
122
+ action = URI.decode(action)
123
+ serves = Event.where(:category => "#{category}-serve", :action => "#{action}").where(date_range).group(:label).order("count_label DESC").count(:label)
124
+ clicks = Event.where(:category => "#{category}", :action => "#{action}").where(date_range).group(:label).order("count_label DESC").count(:label)
125
+
126
+ @top_spot_detail = {}
127
+
128
+
129
+ serves.each do |label|
130
+ serve_count = label[1].nil? ? 0:label[1]
131
+ click_count = clicks[label[0].downcase].nil? ? 0:clicks[label[0].downcase]
132
+ @top_spot_detail[label[0]] = [serve_count, click_count, (click_count/serve_count.to_f)*100]
133
+ end
134
+ end
135
+
136
+ def base_query(category)
137
+ serves = Event.where(date_range).where(:category => "#{category}-serve").group(:action).order("count_category DESC").count(:category)
138
+ clicks = Event.where(date_range).where(:category => "#{category}").group(:action).count(:category)
139
+
140
+ result = []
141
+ serves.each do |data, count|
142
+ unless clicks[data].nil?
143
+ click_count = clicks[data]
144
+ else
145
+ click_count = 0
146
+ end
147
+
148
+ row = {"action" => data,
149
+ "servecount" => count,
150
+ "clickcount" => click_count,
151
+ "click_serve_ratio" => (click_count/count)*100 }
152
+ result << row
153
+ end
154
+
155
+ result
156
+ end
157
+
158
+ def start_date
159
+ if params[:start_date]
160
+ # If there's a specified start date, use it
161
+ @start_date = convert_to_time(params[:start_date])
162
+ else
163
+ # otherwise use 6 months ago as a default, or the earliest event date if logs are younger
164
+ first_date = Event.first[:created_at]
165
+ start = Time.current - 6.months
166
+
167
+ if start < first_date
168
+ @start_date = first_date
169
+ else
170
+ @start_date = start
171
+ end
172
+ end
173
+
174
+ end
175
+
176
+ def end_date
177
+ if params[:end_date]
178
+ # use end date, if specified
179
+ @end_date = convert_to_time(params[:end_date])
180
+ else
181
+ # otherwise use the current date as default
182
+ @end_date = Time.current
183
+ end
184
+ end
185
+
186
+ def convert_to_time(date_input)
187
+ year = date_input[:year]
188
+ month = date_input[:month]
189
+ day = date_input[:day]
190
+
191
+ Time.new(year, month, day)
192
+ end
193
+
194
+ def days_in_sample
195
+ @days_in_sample = ((@end_date - @start_date) / (24*60*60)).round
196
+ if @days_in_sample < 1
197
+ @days_in_sample = 1
198
+ end
199
+ end
200
+
201
+ def excluded_categories
202
+ "category NOT LIKE \"%serve\" AND category <> \"common-searches\" AND category <> \"result-types\"AND category NOT LIKE \"typeahead-%\""
203
+ end
204
+
205
+ def date_range
206
+ { :created_at => start_date.beginning_of_day..end_date.end_of_day }
207
+ end
208
+
209
+ end
210
+ end
@@ -0,0 +1,9 @@
1
+ module QuickSearch
2
+ class OpensearchController < ApplicationController
3
+
4
+ def opensearch
5
+ response.headers['Content-Type'] = 'application/opensearchdescription+xml; charset=utf-8'
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module QuickSearch
2
+ class PagesController < ApplicationController
3
+
4
+ include Auth
5
+
6
+ before_action :auth, only: [:realtime]
7
+
8
+ def home
9
+ end
10
+
11
+ def about
12
+ end
13
+
14
+ def realtime
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,250 @@
1
+ module QuickSearch
2
+ class SearchController < ApplicationController
3
+ include QuickSearch::SearcherConcern
4
+ include QuickSearch::DoiTrap
5
+ include QuickSearch::OnCampus
6
+ include QuickSearch::QueryParser
7
+ include QuickSearch::EncodeUtf8
8
+ include QuickSearch::QueryFilter
9
+ include QuickSearch::SearcherConfig
10
+
11
+ require 'benchmark_logger'
12
+
13
+ before_filter :doi_trap, :log_query
14
+ after_filter :realtime_message, only: [:index]
15
+ protect_from_forgery except: :log_event
16
+
17
+ def index
18
+ loaded_searches
19
+ @common_searches = common_searches
20
+ http_search
21
+ end
22
+
23
+ # TODO: throw error if required files not in place
24
+ def single_searcher
25
+ searcher_name = params[:searcher_name]
26
+
27
+ searcher_cfg = searcher_config(searcher_name)
28
+ if searcher_cfg and searcher_cfg.has_key? 'loaded_searches'
29
+ additional_services = Array.new(searcher_cfg['loaded_searches'])
30
+ else
31
+ additional_services = []
32
+ end
33
+ loaded_searches(additional_services)
34
+ @common_searches = searcher_cfg['common_searches'] || []
35
+ #TODO: maybe a default template for single-searcher searches?
36
+ http_search(searcher_name, "quick_search/search/#{searcher_name}_search")
37
+ end
38
+
39
+ # The following searches for individual sections of the page.
40
+ # This allows us to do client-side requests in cases where the original server-side
41
+ # request times out or otherwise fails.
42
+ def xhr_search
43
+ endpoint = params[:endpoint]
44
+
45
+ if params[:template] == 'with_paging'
46
+ template = 'xhr_response_with_paging'
47
+ else
48
+ template = 'xhr_response'
49
+ end
50
+
51
+ @query = params_q_scrubbed
52
+ @page = page
53
+ @per_page = per_page(endpoint)
54
+ @offset = offset(@page,@per_page)
55
+
56
+ http_client = HTTPClient.new
57
+ update_searcher_timeout(http_client, endpoint, true)
58
+
59
+ benchmark "%s xhr #{endpoint}" % CGI.escape(@query.to_str) do
60
+
61
+ klass = "QuickSearch::#{endpoint.camelize}Searcher".constantize
62
+ searcher = klass.new(http_client,
63
+ extracted_query(params_q_scrubbed),
64
+ @per_page,
65
+ @offset,
66
+ @page,
67
+ on_campus?(ip),
68
+ extracted_scope(params_q_scrubbed))
69
+ searcher.search
70
+
71
+ searcher_partials = {}
72
+ searcher_cfg = searcher_config(endpoint)
73
+ unless searcher_cfg.blank?
74
+ services = searcher_cfg['services'].blank? ? [] : searcher_cfg['services']
75
+ else
76
+ services = []
77
+ end
78
+ services << endpoint
79
+
80
+ respond_to do |format|
81
+
82
+ format.html {
83
+ services.each do |service|
84
+ service_template = render_to_string(
85
+ :partial => "quick_search/search/#{template}",
86
+ :layout => false,
87
+ :locals => { module_display_name: t("#{endpoint}_search.display_name"),
88
+ searcher: searcher,
89
+ search: '',
90
+ service_name: service
91
+ })
92
+ searcher_partials[service] = service_template
93
+ end
94
+ render :json => searcher_partials
95
+ }
96
+
97
+ format.json {
98
+
99
+ # prevents openstruct object from results being nested inside tables
100
+ # See: http://stackoverflow.com/questions/7835047/collecting-hashes-into-openstruct-creates-table-entry
101
+ result_list = []
102
+ searcher.results.each do |result|
103
+ result_list << result.to_h
104
+ end
105
+
106
+ render :json => { :endpoint => endpoint,
107
+ :per_page => @per_page.to_s,
108
+ :page => @page.to_s,
109
+ :total => searcher.total,
110
+ :results => result_list
111
+ }
112
+ }
113
+ end
114
+ end
115
+ end
116
+
117
+ def log_search
118
+ if params[:query].present? && params[:page].present?
119
+ Search.create(query: params[:query], page: params[:page])
120
+ render :nothing => true, :status => 200, :content_type => 'text/html'
121
+ else
122
+ render :nothing => true, :status => 500, :content_type => 'text/html'
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
+ render :nothing => true, :status => 200, :content_type => 'text/javascript'
131
+ else
132
+ render :nothing => true, :status => 200, :content_type => 'text/html'
133
+ end
134
+ else
135
+ render :nothing => true, :status => 500, :content_type => 'text/html'
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def http_search(endpoint = 'defaults', page_to_render = :index)
142
+ @ip = request.remote_ip
143
+
144
+ @search_form_placeholder = I18n.t "#{endpoint}_search.search_form_placeholder"
145
+ @page_title = I18n.t "#{endpoint}_search.display_name"
146
+ @module_callout = I18n.t "#{endpoint}_search.module_callout"
147
+
148
+ if search_in_params?
149
+ @query = params_q_scrubbed
150
+ @search_in_params = true
151
+ search_all_in_threads(endpoint)
152
+ #log_search(@query, page_to_render)
153
+ render page_to_render
154
+ else
155
+ @search_in_params = false
156
+ render '/quick_search/pages/home'
157
+ end
158
+ end
159
+
160
+ def page
161
+ if page_in_params?
162
+ page = params[:page].to_i
163
+ else
164
+ page = 1
165
+ end
166
+ page
167
+ end
168
+ helper_method :page
169
+
170
+ def per_page(endpoint)
171
+ searcher_cfg = searcher_config(endpoint)
172
+ if params[:per_page]
173
+ per_page = params[:per_page].to_i
174
+ elsif params[:template] == 'with_paging'
175
+ if searcher_cfg and searcher_cfg.has_key? 'with_paging'
176
+ per_page = searcher_cfg['with_paging']['per_page']
177
+ else
178
+ per_page = 10
179
+ end
180
+ else
181
+ per_page = QuickSearch::Engine::APP_CONFIG['per_page']
182
+ end
183
+
184
+ if per_page > QuickSearch::Engine::APP_CONFIG['max_per_page']
185
+ per_page = QuickSearch::Engine::APP_CONFIG['max_per_page']
186
+ end
187
+
188
+ per_page
189
+ end
190
+
191
+ def offset(page, per_page)
192
+ (page * per_page) - per_page
193
+ end
194
+
195
+ def page_in_params?
196
+ params[:page] && !params[:page].blank?
197
+ end
198
+
199
+ def search_in_params?
200
+ params_q_scrubbed && !params_q_scrubbed.blank?
201
+ end
202
+ helper_method :search_in_params?
203
+
204
+ def common_searches
205
+ QuickSearch::Engine::APP_CONFIG['common_searches']
206
+ end
207
+
208
+ def loaded_searches(additional_services=[])
209
+ @search_services_for_display = []
210
+ @extracted_query = extracted_query(params_q_scrubbed)
211
+ search_services = additional_services + Array.new(QuickSearch::Engine::APP_CONFIG['loaded_searches'])
212
+
213
+ search_services.each do |search_service|
214
+ if search_in_params?
215
+ @search_services_for_display << {'name' => search_service['name'], 'link'=> search_service['query'] + extracted_query(params_q_scrubbed)}
216
+ else
217
+ @search_services_for_display << {'name' => search_service['name'], 'link'=> search_service['landing_page']}
218
+ end
219
+ end
220
+ end
221
+
222
+ def realtime_message
223
+ if base_url = QuickSearch::Engine::APP_CONFIG['realtime_url']
224
+ begin
225
+ client = HTTPClient.new
226
+ body = {q: params_q_scrubbed}
227
+ url = File.join(base_url, "/message/quicksearch-#{Rails.env}")
228
+ res = client.post(url, body)
229
+ rescue
230
+ end
231
+ end
232
+ end
233
+
234
+ def benchmark(message)
235
+ result = nil
236
+ ms = Benchmark.ms { result = yield }
237
+ BenchmarkLogger.info '%s (%.1fms)' % [ message, ms ]
238
+ result
239
+ end
240
+
241
+ def log_query
242
+ if search_in_params?
243
+ @log_query = filter_query(params_q_scrubbed)
244
+ else
245
+ @log_query = ""
246
+ end
247
+ end
248
+
249
+ end
250
+ end
@@ -0,0 +1,68 @@
1
+ module QuickSearch::ApplicationHelper
2
+ include QuickSearch::EncodeUtf8
3
+
4
+ def application_name
5
+ if action_name == "website"
6
+ I18n.t "website_search.title_name"
7
+ else
8
+ I18n.t "defaults_search.title_name"
9
+ end
10
+ end
11
+
12
+ def organization_name
13
+ I18n.t 'organization_name'
14
+ end
15
+
16
+ def sanitized_query
17
+ params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : sanitize(params_q_scrubbed)
18
+ end
19
+
20
+ def display_query
21
+ params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : truncate(params_q_scrubbed, length: 40, separator: ' ')
22
+ end
23
+
24
+ def log_query
25
+ params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : truncate(sanitize(params_q_scrubbed), length: 100, separator: ' ')
26
+ end
27
+
28
+ def title(page_title = nil)
29
+ query = params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : truncate(params_q_scrubbed, length: 40, separator: ' ', escape: false)
30
+ page_title ||= ''
31
+ page_title << "#{query} |" unless query.blank?
32
+ page_title << " #{application_name} |" unless application_name.blank?
33
+ page_title << " #{organization_name}" unless organization_name.blank?
34
+
35
+ content_for :title, page_title
36
+ end
37
+
38
+ # FIXME this should use en definitions
39
+ def pages(page)
40
+ format_pages ||= ''
41
+ if page.count('-') > 0
42
+ format_pages << "pp.&nbsp;"
43
+ else
44
+ format_pages << "p.&nbsp;"
45
+ end
46
+ format_pages << page
47
+ end
48
+
49
+ def render_module(searcher, service_name, partial_name = '/quick_search/search/module', per_page = 3)
50
+ render partial: partial_name , locals: { module_display_name: t("#{service_name}_search.display_name"), searcher: searcher, search: '', service_name: service_name, per_page: per_page }
51
+ end
52
+
53
+ def get_best_bets_data(best_bets_searcher)
54
+ best_bets = {}
55
+ best_bets[:id] = best_bets_searcher.results.first.id
56
+ best_bets[:title] = best_bets_searcher.results.first.title
57
+ best_bets[:link] = best_bets_searcher.results.first.link
58
+ best_bets[:description] = best_bets_searcher.results.first.description
59
+ best_bets[:best_bets_type] = best_bets_searcher.results.first.best_bets_type
60
+
61
+ best_bets
62
+ end
63
+
64
+ def body_class
65
+ [controller_name, action_name].join('-')
66
+ end
67
+
68
+ end
@@ -0,0 +1,2 @@
1
+ module QuickSearch::AppstatsHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module QuickSearch::OpensearchHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module QuickSearch::PagesHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module QuickSearch::SearchHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ class Event < ActiveRecord::Base
2
+ end
@@ -0,0 +1,11 @@
1
+ class Search < ActiveRecord::Base
2
+
3
+ def self.get_most_frequent_searches
4
+ frequent_searches = []
5
+ Search.all(:select => "query, count(query) as query_count", :group => "query", :order => "query_count DESC", :limit => 7).each do |row|
6
+ frequent_searches << row[:query]
7
+ end
8
+ frequent_searches
9
+ end
10
+
11
+ end
@@ -0,0 +1,102 @@
1
+ module QuickSearch
2
+ class BestBetsSearcher < QuickSearch::Searcher
3
+
4
+ def search
5
+ search_best_bets_index
6
+ end
7
+
8
+ def results
9
+ if results_list
10
+ results_list
11
+ else
12
+ @results_list = []
13
+ unless @response.blank?
14
+ @results_list << @response
15
+ end
16
+ @results_list
17
+ end
18
+ end
19
+
20
+ def total
21
+ results.size
22
+ end
23
+
24
+ private
25
+
26
+ # Regular Best Bets
27
+
28
+ def search_best_bets_index
29
+ response = @http.get(query_url)
30
+ parsed_response = JSON.parse(response.body)
31
+ if parsed_response['response']['numFound'].to_s == '0'
32
+ return nil
33
+ else
34
+ resp = parsed_response['response']['docs'][0]
35
+ result = OpenStruct.new
36
+ result.title = title(resp)
37
+ result.link = link(resp)
38
+ result.id = id(resp)
39
+ result.description = description(resp)
40
+ result.best_bets_type = 'best-bets-regular'
41
+
42
+ @response = result
43
+ end
44
+ end
45
+
46
+ def base_url
47
+ QuickSearch::Engine::APP_CONFIG['best_bets']['solr_url']
48
+ end
49
+
50
+ def query_url
51
+ if http_request_queries['not_escaped'].split.size < 3
52
+ # use non-analyzed keyword field with phrased search
53
+ params = {
54
+ 'q' => %Q(keywords: "#{query_quotes_stripped}")
55
+ }
56
+ else
57
+ # use DisMax query parser for longer queries
58
+ params = {
59
+ 'defType' => 'dismax',
60
+ 'q' => query_quotes_stripped
61
+ }
62
+ if http_request_queries['not_escaped'].split.size < 6
63
+ params['mm'] = '4'
64
+ else
65
+ params['mm'] = '6'
66
+ end
67
+ end
68
+
69
+ # return results in json
70
+ params['wt'] = 'json'
71
+ base_url + params.to_query
72
+ end
73
+
74
+ def title(value)
75
+ value['title']
76
+ end
77
+
78
+ def link(value)
79
+ # TODO: should we make all URLS in the best_bets index absolute? currently some begin with a /
80
+ if value['url'].start_with?('/')
81
+ QuickSearch::Engine::APP_CONFIG['website']['website_base_url'] + value['url']
82
+ else
83
+ value['url']
84
+ end
85
+ end
86
+
87
+ def id(value)
88
+ value['id']
89
+ end
90
+
91
+ def description(value)
92
+ value['description']
93
+ end
94
+
95
+ # General Methods
96
+
97
+ def query_quotes_stripped
98
+ http_request_queries['not_escaped'].gsub('"', '')
99
+ end
100
+
101
+ end
102
+ end