quick_search-core 0.0.1.test

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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