blacklight-spotlight 0.31.0 → 0.32.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/spotlight/dashboards_controller.rb +1 -0
- data/app/controllers/spotlight/resources_controller.rb +1 -1
- data/app/helpers/spotlight/browse_helper.rb +2 -0
- data/app/helpers/spotlight/pages_helper.rb +3 -1
- data/app/helpers/spotlight/rendering_helper.rb +7 -0
- data/app/jobs/spotlight/reindex_job.rb +30 -4
- data/app/models/concerns/spotlight/solr_document.rb +1 -1
- data/app/models/concerns/spotlight/user.rb +1 -0
- data/app/models/spotlight/exhibit.rb +8 -3
- data/app/models/spotlight/reindex_progress.rb +19 -29
- data/app/models/spotlight/reindexing_log_entry.rb +39 -0
- data/app/models/spotlight/resource.rb +10 -59
- data/app/serializers/spotlight/exhibit_export_serializer.rb +7 -7
- data/app/serializers/spotlight/page_representer.rb +1 -1
- data/app/views/spotlight/browse/_search.html.erb +1 -1
- data/app/views/spotlight/browse/show.html.erb +1 -1
- data/app/views/spotlight/catalog/_edit_default.html.erb +2 -1
- data/app/views/spotlight/dashboards/_reindexing_activity.html.erb +28 -0
- data/app/views/spotlight/dashboards/show.html.erb +4 -0
- data/config/locales/spotlight.en.yml +15 -0
- data/db/migrate/20170105222939_create_spotlight_reindexing_log_entries.rb +15 -0
- data/lib/spotlight/version.rb +1 -1
- data/spec/examples.txt +1066 -8
- data/spec/factories/reindexing_log_entries.rb +52 -0
- data/spec/features/javascript/reindex_monitor_spec.rb +3 -3
- data/spec/jobs/spotlight/reindex_job_spec.rb +37 -0
- data/spec/models/spotlight/exhibit_spec.rb +43 -4
- data/spec/models/spotlight/reindex_progress_spec.rb +128 -96
- data/spec/models/spotlight/reindexing_log_entry_spec.rb +135 -0
- data/spec/models/spotlight/resource_spec.rb +16 -28
- data/spec/support/views/test_view_helpers.rb +1 -0
- data/spec/views/spotlight/browse/show.html.erb_spec.rb +6 -0
- data/spec/views/spotlight/dashboards/_reindexing_activity.html.erb_spec.rb +87 -0
- metadata +26 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1fd2b950f32f603a3ee0f2db98c3b91e5cad5f9
|
4
|
+
data.tar.gz: 6cb02f70515f59a51422ef7cf1309216351b091f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 113fc7cd7d398ecde9d12c8428aeee286b0604267ac11e4222656766760620a11f800a33484d6c1878590f94f367443794d9baa49facaeca9adfb318a26c6a04
|
7
|
+
data.tar.gz: c29d9d9a84bedd527d3c63fdc0fa6d2c9ca648d69f54eda3b0b1be77843649329efc526832e6bdc8a98570b9ca8ac02627433c9300f675640eba1469d165b092
|
@@ -3,6 +3,8 @@ module Spotlight
|
|
3
3
|
# Helper for browse views
|
4
4
|
module BrowseHelper
|
5
5
|
include ::BlacklightConfigurationHelper
|
6
|
+
include Spotlight::RenderingHelper
|
7
|
+
|
6
8
|
def document_index_view_type
|
7
9
|
if @search && @search.default_index_view_type.present? && params[:view].blank?
|
8
10
|
blacklight_config.view[@search.default_index_view_type].key
|
@@ -2,6 +2,8 @@ module Spotlight
|
|
2
2
|
##
|
3
3
|
# Sir-trevor helpers methods
|
4
4
|
module PagesHelper
|
5
|
+
include Spotlight::RenderingHelper
|
6
|
+
|
5
7
|
##
|
6
8
|
# Override the default #sir_trevor_markdown so we can use
|
7
9
|
# a more complete markdown rendered
|
@@ -12,7 +14,7 @@ module Spotlight
|
|
12
14
|
''
|
13
15
|
end
|
14
16
|
|
15
|
-
|
17
|
+
render_markdown(clean_text)
|
16
18
|
end
|
17
19
|
|
18
20
|
def available_index_fields
|
@@ -4,12 +4,34 @@ module Spotlight
|
|
4
4
|
class ReindexJob < ActiveJob::Base
|
5
5
|
queue_as :default
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
before_perform do |job|
|
8
|
+
job_log_entry = log_entry(job)
|
9
|
+
next unless job_log_entry
|
10
|
+
|
11
|
+
items_reindexed_estimate = resource_list(job.arguments.first).sum do |resource|
|
12
|
+
resource.document_builder.documents_to_index.size
|
13
|
+
end
|
14
|
+
job_log_entry.update(items_reindexed_estimate: items_reindexed_estimate)
|
15
|
+
end
|
16
|
+
|
17
|
+
around_perform do |job, block|
|
18
|
+
job_log_entry = log_entry(job)
|
19
|
+
job_log_entry.in_progress! if job_log_entry
|
20
|
+
|
21
|
+
begin
|
22
|
+
block.call
|
23
|
+
rescue
|
24
|
+
job_log_entry.failed! if job_log_entry
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
|
28
|
+
job_log_entry.succeeded! if job_log_entry
|
9
29
|
end
|
10
30
|
|
11
|
-
def perform(exhibit_or_resources)
|
12
|
-
resource_list(exhibit_or_resources).each
|
31
|
+
def perform(exhibit_or_resources, log_entry = nil)
|
32
|
+
resource_list(exhibit_or_resources).each do |resource|
|
33
|
+
resource.reindex(log_entry)
|
34
|
+
end
|
13
35
|
end
|
14
36
|
|
15
37
|
private
|
@@ -23,5 +45,9 @@ module Spotlight
|
|
23
45
|
Array(exhibit_or_resources)
|
24
46
|
end
|
25
47
|
end
|
48
|
+
|
49
|
+
def log_entry(job)
|
50
|
+
job.arguments.second if job.arguments.second.is_a?(Spotlight::ReindexingLogEntry)
|
51
|
+
end
|
26
52
|
end
|
27
53
|
end
|
@@ -86,7 +86,7 @@ module Spotlight
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def to_solr
|
89
|
-
{ self.class.unique_key.to_sym => id }.reverse_merge(sidecars.inject({}) { |
|
89
|
+
{ self.class.unique_key.to_sym => id }.reverse_merge(sidecars.inject({}) { |acc, elem| acc.merge(elem.to_solr) }).merge(tags_to_solr)
|
90
90
|
end
|
91
91
|
|
92
92
|
def make_public!(exhibit)
|
@@ -6,6 +6,7 @@ module Spotlight
|
|
6
6
|
included do
|
7
7
|
has_many :roles, class_name: 'Spotlight::Role', dependent: :destroy
|
8
8
|
has_many :exhibits, class_name: 'Spotlight::Exhibit', through: :roles, source: 'resource', source_type: 'Spotlight::Exhibit'
|
9
|
+
has_many :reindexing_log_entries, class_name: 'Spotlight::ReindexingLogEntry'
|
9
10
|
|
10
11
|
scope :with_roles, -> { where(id: Spotlight::Role.distinct.pluck(:user_id)) }
|
11
12
|
|
@@ -32,6 +32,7 @@ module Spotlight
|
|
32
32
|
has_many :feature_pages, extend: FriendlyId::FinderMethods
|
33
33
|
has_many :main_navigations, dependent: :delete_all
|
34
34
|
has_many :owned_taggings, class_name: 'ActsAsTaggableOn::Tagging', as: :tagger
|
35
|
+
has_many :reindexing_log_entries, dependent: :destroy
|
35
36
|
has_many :resources
|
36
37
|
has_many :roles, as: :resource, dependent: :delete_all
|
37
38
|
has_many :searches, dependent: :destroy, extend: FriendlyId::FinderMethods
|
@@ -79,8 +80,8 @@ module Spotlight
|
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
82
|
-
def reindex_later
|
83
|
-
Spotlight::ReindexJob.perform_later(self)
|
83
|
+
def reindex_later(user = nil)
|
84
|
+
Spotlight::ReindexJob.perform_later(self, new_reindexing_log_entry(user))
|
84
85
|
end
|
85
86
|
|
86
87
|
def uploaded_resource_fields
|
@@ -100,7 +101,7 @@ module Spotlight
|
|
100
101
|
end
|
101
102
|
|
102
103
|
def reindex_progress
|
103
|
-
@reindex_progress ||= ReindexProgress.new(
|
104
|
+
@reindex_progress ||= ReindexProgress.new(self)
|
104
105
|
end
|
105
106
|
|
106
107
|
protected
|
@@ -108,5 +109,9 @@ module Spotlight
|
|
108
109
|
def sanitize_description
|
109
110
|
self.description = ::Rails::Html::FullSanitizer.new.sanitize(description)
|
110
111
|
end
|
112
|
+
|
113
|
+
def new_reindexing_log_entry(user = nil)
|
114
|
+
Spotlight::ReindexingLogEntry.create(exhibit: self, user: user, items_reindexed_count: 0, job_status: 'unstarted')
|
115
|
+
end
|
111
116
|
end
|
112
117
|
end
|
@@ -2,51 +2,47 @@ module Spotlight
|
|
2
2
|
##
|
3
3
|
# ReindexProgress is a class that models the progress of reindexing a list of resources
|
4
4
|
class ReindexProgress
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
Spotlight::Resource.none
|
10
|
-
end
|
5
|
+
attr_reader :exhibit
|
6
|
+
|
7
|
+
def initialize(exhibit)
|
8
|
+
@exhibit = exhibit
|
11
9
|
end
|
12
10
|
|
13
11
|
def recently_in_progress?
|
14
|
-
|
12
|
+
return false if current_log_entry.blank?
|
13
|
+
return true if current_log_entry.in_progress?
|
14
|
+
|
15
|
+
current_log_entry.end_time.present? && (current_log_entry.end_time > Spotlight::Engine.config.reindex_progress_window.minutes.ago)
|
15
16
|
end
|
16
17
|
|
17
18
|
def started_at
|
18
|
-
|
19
|
-
|
20
|
-
enqueued_resources = resources.select(&:enqueued_at?)
|
21
|
-
|
22
|
-
return unless enqueued_resources.any?
|
23
|
-
|
24
|
-
@started ||= enqueued_resources.min_by(&:enqueued_at).enqueued_at
|
19
|
+
current_log_entry.try(:start_time)
|
25
20
|
end
|
26
21
|
|
27
22
|
def updated_at
|
28
|
-
|
23
|
+
current_log_entry.try(:updated_at)
|
29
24
|
end
|
30
25
|
|
31
26
|
def finished?
|
32
|
-
|
27
|
+
return false if current_log_entry.blank?
|
28
|
+
current_log_entry.succeeded? || current_log_entry.failed?
|
33
29
|
end
|
34
30
|
|
35
31
|
def finished_at
|
36
|
-
|
37
|
-
@finished ||= completed_resources.max_by(&:last_indexed_finished).last_indexed_finished
|
32
|
+
current_log_entry.try(:end_time)
|
38
33
|
end
|
39
34
|
|
40
35
|
def total
|
41
|
-
|
36
|
+
current_log_entry.try(:items_reindexed_estimate)
|
42
37
|
end
|
43
38
|
|
44
39
|
def completed
|
45
|
-
|
40
|
+
current_log_entry.try(:items_reindexed_count)
|
46
41
|
end
|
47
42
|
|
48
43
|
def errored?
|
49
|
-
|
44
|
+
return false if current_log_entry.blank?
|
45
|
+
current_log_entry.failed?
|
50
46
|
end
|
51
47
|
|
52
48
|
def as_json(*)
|
@@ -63,10 +59,8 @@ module Spotlight
|
|
63
59
|
|
64
60
|
private
|
65
61
|
|
66
|
-
|
67
|
-
|
68
|
-
def any_waiting?
|
69
|
-
resources.any?(&:waiting?)
|
62
|
+
def current_log_entry
|
63
|
+
exhibit.reindexing_log_entries.where.not(job_status: 'unstarted').first
|
70
64
|
end
|
71
65
|
|
72
66
|
def localized_start_time
|
@@ -83,9 +77,5 @@ module Spotlight
|
|
83
77
|
return unless updated_at
|
84
78
|
I18n.l(updated_at, format: :short)
|
85
79
|
end
|
86
|
-
|
87
|
-
def completed_resources
|
88
|
-
resources.completed
|
89
|
-
end
|
90
80
|
end
|
91
81
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Spotlight
|
2
|
+
##
|
3
|
+
# a log entry representing an attempt to reindex some number of records in an exhibit
|
4
|
+
class ReindexingLogEntry < ActiveRecord::Base
|
5
|
+
enum job_status: { unstarted: 0, in_progress: 1, succeeded: 2, failed: 3 }
|
6
|
+
|
7
|
+
belongs_to :exhibit, class_name: 'Spotlight::Exhibit'
|
8
|
+
belongs_to :user, class_name: '::User'
|
9
|
+
|
10
|
+
# null start times sort to the top, to more easily surface pending reindexing
|
11
|
+
default_scope { order('start_time IS NOT NULL, start_time DESC') }
|
12
|
+
scope :recent, -> { limit(5) }
|
13
|
+
|
14
|
+
def duration
|
15
|
+
end_time - start_time if end_time
|
16
|
+
end
|
17
|
+
|
18
|
+
def in_progress!
|
19
|
+
self.start_time = Time.zone.now
|
20
|
+
super
|
21
|
+
rescue
|
22
|
+
Rails.logger.error "unexpected error updating log entry to :in_progress from #{caller}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def succeeded!
|
26
|
+
self.end_time = Time.zone.now
|
27
|
+
super
|
28
|
+
rescue
|
29
|
+
Rails.logger.error "unexpected error updating log entry to :succeeded from #{caller}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def failed!
|
33
|
+
self.end_time = Time.zone.now
|
34
|
+
super
|
35
|
+
rescue
|
36
|
+
Rails.logger.error "unexpected error updating log entry to :failed from #{caller}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -16,19 +16,9 @@ module Spotlight
|
|
16
16
|
has_many :solr_document_sidecars
|
17
17
|
|
18
18
|
serialize :data, Hash
|
19
|
-
store :metadata, accessors: [
|
20
|
-
:enqueued_at,
|
21
|
-
:last_indexed_estimate,
|
22
|
-
:last_indexed_count,
|
23
|
-
:last_index_elapsed_time,
|
24
|
-
:last_indexed_finished
|
25
|
-
], coder: JSON
|
26
19
|
|
27
|
-
enum index_status: [:waiting, :completed, :errored]
|
28
|
-
|
29
|
-
around_index :reindex_with_logging
|
30
20
|
after_index :commit
|
31
|
-
after_index :
|
21
|
+
after_index :touch_exhibit!
|
32
22
|
|
33
23
|
##
|
34
24
|
# Persist the record to the database, and trigger a reindex to solr
|
@@ -41,27 +31,9 @@ module Spotlight
|
|
41
31
|
##
|
42
32
|
# Enqueue an asynchronous reindexing job for this resource
|
43
33
|
def reindex_later
|
44
|
-
waiting!
|
45
34
|
Spotlight::ReindexJob.perform_later(self)
|
46
35
|
end
|
47
36
|
|
48
|
-
def waiting!
|
49
|
-
update(enqueued_at: Time.zone.now)
|
50
|
-
super
|
51
|
-
end
|
52
|
-
|
53
|
-
def enqueued_at
|
54
|
-
cast_to_date_time(super)
|
55
|
-
end
|
56
|
-
|
57
|
-
def enqueued_at?
|
58
|
-
enqueued_at.present?
|
59
|
-
end
|
60
|
-
|
61
|
-
def last_indexed_finished
|
62
|
-
cast_to_date_time(super)
|
63
|
-
end
|
64
|
-
|
65
37
|
def document_model
|
66
38
|
exhibit.blacklight_config.document_model if exhibit
|
67
39
|
end
|
@@ -71,43 +43,28 @@ module Spotlight
|
|
71
43
|
# Index the result of {#to_solr} into the index in batches of {#batch_size}
|
72
44
|
#
|
73
45
|
# @return [Integer] number of records indexed
|
74
|
-
|
46
|
+
# rubocop:disable Metrics/MethodLength
|
47
|
+
def reindex(reindexing_log_entry = nil)
|
75
48
|
benchmark "Reindexing #{self} (batch size: #{batch_size})" do
|
76
49
|
count = 0
|
77
50
|
|
78
51
|
run_callbacks :index do
|
79
52
|
document_builder.documents_to_index.each_slice(batch_size) do |batch|
|
80
53
|
write_to_index(batch)
|
81
|
-
|
54
|
+
count += batch.length
|
55
|
+
reindexing_log_entry.update(items_reindexed_count: count) if reindexing_log_entry
|
82
56
|
end
|
83
57
|
|
84
58
|
count
|
85
59
|
end
|
86
60
|
end
|
87
61
|
end
|
62
|
+
# rubocop:enable Metrics/MethodLength
|
88
63
|
|
89
64
|
def document_builder
|
90
65
|
@document_builder ||= document_builder_class.new(self)
|
91
66
|
end
|
92
67
|
|
93
|
-
protected
|
94
|
-
|
95
|
-
def reindex_with_logging
|
96
|
-
time_start = Time.zone.now
|
97
|
-
|
98
|
-
update(indexed_at: time_start,
|
99
|
-
last_indexed_estimate: document_builder.documents_to_index.size,
|
100
|
-
last_indexed_finished: nil,
|
101
|
-
last_index_elapsed_time: nil)
|
102
|
-
|
103
|
-
count = yield
|
104
|
-
|
105
|
-
time_end = Time.zone.now
|
106
|
-
update(last_indexed_count: count,
|
107
|
-
last_indexed_finished: time_end,
|
108
|
-
last_index_elapsed_time: time_end - time_start)
|
109
|
-
end
|
110
|
-
|
111
68
|
private
|
112
69
|
|
113
70
|
def blacklight_solr
|
@@ -136,18 +93,12 @@ module Spotlight
|
|
136
93
|
Rails.logger.warn "Unable to commit to solr: #{e}"
|
137
94
|
end
|
138
95
|
|
139
|
-
def
|
140
|
-
|
96
|
+
def touch_exhibit!
|
97
|
+
exhibit.touch
|
141
98
|
end
|
142
99
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
146
|
-
if defined? ActiveModel::Type::DateTime
|
147
|
-
ActiveModel::Type::DateTime.new.cast(value)
|
148
|
-
else
|
149
|
-
ActiveRecord::Type::DateTime.new.type_cast_from_database(value)
|
150
|
-
end
|
100
|
+
def write?
|
101
|
+
Spotlight::Engine.config.writable_index
|
151
102
|
end
|
152
103
|
end
|
153
104
|
end
|
@@ -33,7 +33,7 @@ module Spotlight
|
|
33
33
|
property prop
|
34
34
|
end
|
35
35
|
|
36
|
-
collection :searches,
|
36
|
+
collection :searches, populator: ->(fragment, options) { options[:represented].searches.find_or_initialize_by(slug: fragment['slug']) },
|
37
37
|
class: Spotlight::Search do
|
38
38
|
(Spotlight::Search.attribute_names - %w(id scope exhibit_id masthead_id thumbnail_id)).each do |prop|
|
39
39
|
property prop
|
@@ -44,16 +44,16 @@ module Spotlight
|
|
44
44
|
property :thumbnail, class: Spotlight::FeaturedImage, decorator: FeaturedImageRepresenter
|
45
45
|
end
|
46
46
|
|
47
|
-
collection :about_pages,
|
47
|
+
collection :about_pages, populator: ->(fragment, options) { options[:represented].about_pages.find_or_initialize_by(slug: fragment['slug']) },
|
48
48
|
class: Spotlight::AboutPage,
|
49
49
|
decorator: PageRepresenter
|
50
50
|
|
51
|
-
collection :feature_pages,
|
51
|
+
collection :feature_pages, populator: ->(fragment, options) { options[:represented].feature_pages.find_or_initialize_by(slug: fragment['slug']) },
|
52
52
|
getter: ->(_opts) { feature_pages.at_top_level },
|
53
53
|
class: Spotlight::FeaturePage,
|
54
54
|
decorator: NestedPageRepresenter
|
55
55
|
|
56
|
-
property :home_page,
|
56
|
+
property :home_page, populator: ->(_fragment, options) { options[:represented].home_page },
|
57
57
|
class: Spotlight::HomePage,
|
58
58
|
decorator: PageRepresenter
|
59
59
|
|
@@ -65,14 +65,14 @@ module Spotlight
|
|
65
65
|
|
66
66
|
property :blacklight_configuration, class: Spotlight::BlacklightConfiguration, decorator: ConfigurationRepresenter
|
67
67
|
|
68
|
-
collection :custom_fields,
|
68
|
+
collection :custom_fields, populator: ->(fragment, options) { options[:represented].custom_fields.find_or_initialize_by(slug: fragment['slug']) },
|
69
69
|
class: Spotlight::CustomField do
|
70
70
|
(Spotlight::CustomField.attribute_names - %w(id exhibit_id)).each do |prop|
|
71
71
|
property prop
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
collection :contacts,
|
75
|
+
collection :contacts, populator: ->(fragment, options) { options[:represented].contacts.find_or_initialize_by(slug: fragment['slug']) },
|
76
76
|
class: Spotlight::Contact do
|
77
77
|
(Spotlight::Contact.attribute_names - %w(id exhibit_id avatar)).each do |prop|
|
78
78
|
property prop
|
@@ -150,7 +150,7 @@ module Spotlight
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
-
collection :resources, class: ->(
|
153
|
+
collection :resources, class: ->(options) { options[:fragment].key?('type') ? options[:fragment]['type'].constantize : Spotlight::Resource } do
|
154
154
|
(Spotlight::Resource.attribute_names - %w(id url exhibit_id)).each do |prop|
|
155
155
|
property prop
|
156
156
|
end
|