diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9933617c..6bcec29c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,18 +6,24 @@ class ApplicationController < ActionController::Base # We set this in a session cookie to persist user preference across searches. # Clicking on a different tab will update the cookie. def set_active_tab - @active_tab = if Feature.enabled?(:geodata) - # Determine which tab to load - default to primo unless gdt is enabled - 'geodata' - elsif params[:tab].present? - # If params[:tab] is set, use it and set session + # GeoData doesn't use the tab system. + return if Feature.enabled?(:geodata) + + @active_tab = if params[:tab].present? && valid_tab?(params[:tab]) + # If params[:tab] is set and valid, use it and set session cookies[:last_tab] = params[:tab] - elsif cookies[:last_tab].present? - # Otherwise, check for last used tab in session + elsif cookies[:last_tab].present? && valid_tab?(cookies[:last_tab]) + # Otherwise, check for last used tab in session if valid cookies[:last_tab] else # Default behavior when no tab is specified in params or session - cookies[:last_tab] = 'primo' + cookies[:last_tab] = 'all' end end + + private + + def valid_tab?(tab) + %w[primo timdex all].include?(tab) + end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index d5ef583c..3157eab0 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -17,15 +17,21 @@ def results @enhanced_query = Enhancer.new(params).enhanced_query + # Load GeoData results if applicable + if Feature.enabled?(:geodata) + load_geodata_results + render 'results_geo' + return + end + # Route to appropriate search based on active tab case @active_tab when 'primo' load_primo_results when 'timdex' load_timdex_results - when 'geodata' - load_gdt_results - render 'results_geo' + when 'all' + load_all_results end end @@ -50,7 +56,7 @@ def sleep_if_too_fast sleep(1 - duration) end - def load_gdt_results + def load_geodata_results query = QueryBuilder.new(@enhanced_query).query response = query_timdex(query) @@ -66,49 +72,96 @@ def load_gdt_results end def load_primo_results + data = fetch_primo_data + @results = data[:results] + @pagination = data[:pagination] + @errors = data[:errors] + @show_primo_continuation = data[:show_continuation] + end + + def load_timdex_results + data = fetch_timdex_data + @results = data[:results] + @pagination = data[:pagination] + @errors = data[:errors] + end + + def load_all_results + # Parallel fetching from both APIs + primo_thread = Thread.new { fetch_primo_data } + timdex_thread = Thread.new { fetch_timdex_data } + + # Wait for both threads to complete + primo_data = primo_thread.value + timdex_data = timdex_thread.value + + # Collect any errors from either API + all_errors = [] + all_errors.concat(primo_data[:errors]) if primo_data[:errors] + all_errors.concat(timdex_data[:errors]) if timdex_data[:errors] + @errors = all_errors.any? ? all_errors : nil + + # Zipper merge results from both APIs + primo_results = primo_data[:results] || [] + timdex_results = timdex_data[:results] || [] + @results = primo_results.zip(timdex_results).flatten.compact + + # For now, just use primo pagination as a placeholder + @pagination = primo_data[:pagination] || {} + + # Handle primo continuation for high page numbers + @show_primo_continuation = primo_data[:show_continuation] || false + end + + def fetch_primo_data current_page = @enhanced_query[:page] || 1 per_page = @enhanced_query[:per_page] || 20 offset = (current_page - 1) * per_page # Check if we're beyond Primo API limits before making the request. if offset >= Analyzer::PRIMO_MAX_OFFSET - @show_primo_continuation = true - return + return { results: [], pagination: {}, errors: nil, show_continuation: true } end primo_response = query_primo - @results = NormalizePrimoResults.new(primo_response, @enhanced_query[:q]).normalize + results = NormalizePrimoResults.new(primo_response, @enhanced_query[:q]).normalize + pagination = Analyzer.new(@enhanced_query, primo_response, :primo).pagination # Handle empty results from Primo API. Sometimes Primo will return no results at a given offset, # despite claiming in the initial query that more are available. This happens randomly and # seemingly for no reason (well below the recommended offset of 2,000). While the bug also # exists in Primo UI, sending users there seems like the best we can do. - if @results.empty? + show_continuation = false + errors = nil + + if results.empty? docs = primo_response['docs'] if primo_response.is_a?(Hash) if docs.nil? || docs.empty? # Only show continuation for pagination scenarios (page > 1), not for searches with no results - @show_primo_continuation = true if current_page > 1 + show_continuation = true if current_page > 1 else - @errors = [{ 'message' => 'No more results available at this page number.' }] + errors = [{ 'message' => 'No more results available at this page number.' }] end end - # Use Analyzer for consistent pagination across all search types - @pagination = Analyzer.new(@enhanced_query, primo_response, :primo).pagination + { results: results, pagination: pagination, errors: errors, show_continuation: show_continuation } rescue StandardError => e - @errors = handle_primo_errors(e) + { results: [], pagination: {}, errors: handle_primo_errors(e), show_continuation: false } end - def load_timdex_results + def fetch_timdex_data query = QueryBuilder.new(@enhanced_query).query response = query_timdex(query) - - @errors = extract_errors(response) - return unless @errors.nil? - - @pagination = Analyzer.new(@enhanced_query, response, :timdex).pagination - raw_results = extract_results(response) - @results = NormalizeTimdexResults.new(raw_results, @enhanced_query[:q]).normalize + errors = extract_errors(response) + + if errors.nil? + pagination = Analyzer.new(@enhanced_query, response, :timdex).pagination + raw_results = extract_results(response) + results = NormalizeTimdexResults.new(raw_results, @enhanced_query[:q]).normalize + { results: results, pagination: pagination, errors: nil } + else + { results: [], pagination: {}, errors: errors } + end end def active_filters @@ -121,9 +174,9 @@ def query_timdex(query) # Builder hands off to wrapper which returns raw results here. Rails.cache.fetch("#{cache_key}/#{@active_tab}", expires_in: 12.hours) do - raw = if @active_tab == 'geodata' + raw = if Feature.enabled?(:geodata) execute_geospatial_query(query) - elsif @active_tab == 'timdex' + elsif @active_tab == 'timdex' || @active_tab == 'all' TimdexBase::Client.query(TimdexSearch::BaseQuery, variables: query) end diff --git a/app/helpers/filter_helper.rb b/app/helpers/filter_helper.rb index ae93d8d7..6fd60696 100644 --- a/app/helpers/filter_helper.rb +++ b/app/helpers/filter_helper.rb @@ -33,7 +33,7 @@ def nice_labels } end - def gdt_sources(value, category) + def geodata_sources(value, category) return value if category != :sourceFilter return 'Non-MIT institutions' if value == 'opengeometadata gis resources' diff --git a/app/javascript/search_form.js b/app/javascript/search_form.js index daddf67f..fa603040 100644 --- a/app/javascript/search_form.js +++ b/app/javascript/search_form.js @@ -57,7 +57,7 @@ function updateKeywordPlaceholder() { } } -// Add event listeners for all panels in the DOM. For GDT, this is currently both geospatial panels and the advanced +// Add event listeners for all panels in the DOM. For GeoData, this is currently both geospatial panels and the advanced // panel. In all other TIMDEX UI apps, it's just the advanced panel. if (Array.from(allPanels).includes(geoboxPanel && geodistancePanel)) { document.getElementById('geobox-summary').addEventListener('click', () => { diff --git a/app/models/feature.rb b/app/models/feature.rb index 171d4ee9..b36fd77e 100644 --- a/app/models/feature.rb +++ b/app/models/feature.rb @@ -11,13 +11,13 @@ # # @example Setting Flags in Environment # # In local ENV or Heroku config: -# FEATURE_GEODATA=true # Enables the GDT feature +# FEATURE_GEODATA=true # Enables the GeoData feature # FEATURE_BOOLEAN_PICKER=true # Enables the boolean picker feature # # # Any non-true value or not setting ENV does not enable the feature -# FEATURE_GEODATA=false # Does not enable the GDT feature -# FEATURE_GEODATA=1 # Does not enable the GDT feature -# FEATURE_GEODATA=on # Does not enable the GDT feature +# FEATURE_GEODATA=false # Does not enable the GeoData feature +# FEATURE_GEODATA=1 # Does not enable the GeoData feature +# FEATURE_GEODATA=on # Does not enable the GeoData feature # # @example Usage in Different Contexts # # In models/controllers: diff --git a/app/views/record/_record_geo.html.erb b/app/views/record/_record_geo.html.erb index aa247a62..165042f5 100644 --- a/app/views/record/_record_geo.html.erb +++ b/app/views/record/_record_geo.html.erb @@ -41,7 +41,7 @@ <% end %> - + <% if @record['publishers'].present? %>

Publishers