blacklight-spotlight 3.0.0.alpha.9 → 3.0.0.rc4
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 +3 -3
- data/app/assets/images/blacklight/arrow-alt-circle-left.svg +1 -0
- data/app/assets/images/blacklight/arrow-alt-circle-right.svg +1 -0
- data/app/assets/javascripts/spotlight/admin/{add_new_page_button.js → add_new_button.js} +7 -0
- data/app/assets/javascripts/spotlight/admin/block_mixins/autocompleteable.js +4 -4
- data/app/assets/javascripts/spotlight/admin/blocks/browse_block.js +55 -1
- data/app/assets/javascripts/spotlight/admin/blocks/browse_group_categories_block.js +88 -0
- data/app/assets/javascripts/spotlight/admin/blocks/pages_block.js +1 -1
- data/app/assets/javascripts/spotlight/admin/blocks/solr_documents_base_block.js +1 -1
- data/app/assets/javascripts/spotlight/admin/blocks/uploaded_items_block.js +7 -2
- data/app/assets/javascripts/spotlight/admin/crop.es6 +11 -0
- data/app/assets/javascripts/spotlight/admin/croppable.js +1 -1
- data/app/assets/javascripts/spotlight/admin/index.js +0 -2
- data/app/assets/javascripts/spotlight/admin/reindex_monitor.js +1 -0
- data/app/assets/javascripts/spotlight/admin/search_typeahead.js +2 -2
- data/app/assets/javascripts/spotlight/admin/sir-trevor/block_controls.js +21 -12
- data/app/assets/javascripts/spotlight/admin/sir-trevor/locales.js +11 -3
- data/app/assets/javascripts/spotlight/user/browse_group_categories.js +59 -0
- data/app/assets/javascripts/spotlight/user/index.js +1 -0
- data/app/assets/stylesheets/spotlight/_accessibility.scss +1 -1
- data/app/assets/stylesheets/spotlight/_breadcrumbs.scss +8 -0
- data/app/assets/stylesheets/spotlight/_browse.scss +16 -0
- data/app/assets/stylesheets/spotlight/_catalog.scss +6 -6
- data/app/assets/stylesheets/spotlight/_curation.scss +6 -0
- data/app/assets/stylesheets/spotlight/_featured_browse_categories_block.scss +214 -83
- data/app/assets/stylesheets/spotlight/_header.scss +1 -1
- data/app/assets/stylesheets/spotlight/_item_text_block.scss +6 -0
- data/app/assets/stylesheets/spotlight/_pages.scss +9 -4
- data/app/assets/stylesheets/spotlight/_report_a_problem.scss +5 -2
- data/app/assets/stylesheets/spotlight/_spotlight.scss +2 -0
- data/app/assets/stylesheets/spotlight/_translations.scss +7 -0
- data/app/assets/stylesheets/spotlight/browse_group_categories_block.scss +92 -0
- data/app/builders/spotlight/bootstrap_breadcrumbs_builder.rb +1 -2
- data/app/controllers/concerns/spotlight/search_helper.rb +2 -8
- data/app/controllers/spotlight/appearances_controller.rb +0 -12
- data/app/controllers/spotlight/browse_controller.rb +7 -3
- data/app/controllers/spotlight/catalog_controller.rb +5 -2
- data/app/controllers/spotlight/concerns/application_controller.rb +13 -2
- data/app/controllers/spotlight/dashboards_controller.rb +1 -1
- data/app/controllers/spotlight/exhibits_controller.rb +2 -3
- data/app/controllers/spotlight/featured_images_controller.rb +1 -1
- data/app/controllers/spotlight/groups_controller.rb +80 -0
- data/app/controllers/spotlight/pages_controller.rb +6 -9
- data/app/controllers/spotlight/resources/csv_upload_controller.rb +1 -1
- data/app/controllers/spotlight/searches_controller.rb +7 -19
- data/app/controllers/spotlight/translations_controller.rb +46 -0
- data/app/helpers/spotlight/application_helper.rb +20 -1
- data/app/helpers/spotlight/crop_helper.rb +4 -1
- data/app/helpers/spotlight/crud_link_helpers.rb +1 -1
- data/app/helpers/spotlight/main_app_helpers.rb +1 -1
- data/app/helpers/spotlight/pages_helper.rb +1 -1
- data/app/jobs/concerns/spotlight/job_tracking.rb +47 -0
- data/app/jobs/concerns/spotlight/limit_concurrency.rb +33 -0
- data/app/jobs/spotlight/add_uploads_from_csv.rb +34 -6
- data/app/jobs/spotlight/application_job.rb +8 -0
- data/app/jobs/spotlight/cleanup_job_trackers_job.rb +13 -0
- data/app/jobs/spotlight/default_thumbnail_job.rb +1 -3
- data/app/jobs/spotlight/reindex_exhibit_job.rb +36 -0
- data/app/jobs/spotlight/reindex_job.rb +49 -41
- data/app/jobs/spotlight/rename_sidecar_field_job.rb +2 -2
- data/app/jobs/spotlight/update_job_trackers_job.rb +20 -0
- data/app/mailers/spotlight/indexing_complete_mailer.rb +3 -2
- data/app/models/concerns/spotlight/exhibit_defaults.rb +1 -1
- data/app/models/concerns/spotlight/translatables.rb +17 -1
- data/app/models/concerns/spotlight/user.rb +2 -1
- data/app/models/sir_trevor_rails/blocks/browse_group_categories_block.rb +25 -0
- data/app/models/spotlight/ability.rb +2 -0
- data/app/models/spotlight/about_page.rb +3 -1
- data/app/models/spotlight/blacklight_configuration.rb +2 -1
- data/app/models/spotlight/contact.rb +1 -1
- data/app/models/spotlight/custom_field.rb +3 -3
- data/app/models/spotlight/event.rb +13 -0
- data/app/models/spotlight/exhibit.rb +18 -14
- data/app/models/spotlight/feature_page.rb +3 -1
- data/app/models/spotlight/featured_image.rb +29 -12
- data/app/models/spotlight/group.rb +22 -0
- data/app/models/spotlight/group_member.rb +11 -0
- data/app/models/spotlight/home_page.rb +3 -1
- data/app/models/spotlight/job_tracker.rb +105 -0
- data/app/models/spotlight/main_navigation.rb +2 -2
- data/app/models/spotlight/masthead.rb +1 -1
- data/app/models/spotlight/page.rb +5 -1
- data/app/models/spotlight/page_configurations.rb +6 -0
- data/app/models/spotlight/page_content.rb +2 -0
- data/app/models/spotlight/reindex_progress.rb +44 -27
- data/app/models/spotlight/resource.rb +24 -58
- data/app/models/spotlight/resources/csv_upload.rb +2 -1
- data/app/models/spotlight/resources/iiif_harvester.rb +10 -1
- data/app/models/spotlight/resources/iiif_manifest.rb +11 -7
- data/app/models/spotlight/resources/iiif_service.rb +1 -1
- data/app/models/spotlight/resources/json_upload.rb +12 -0
- data/app/models/spotlight/resources/upload.rb +25 -2
- data/app/models/spotlight/search.rb +10 -1
- data/app/models/spotlight/solr_document_sidecar.rb +9 -6
- data/app/models/spotlight/temporary_image.rb +8 -0
- data/app/services/spotlight/etl.rb +7 -0
- data/app/services/spotlight/etl/context.rb +52 -0
- data/app/services/spotlight/etl/executor.rb +194 -0
- data/app/services/spotlight/etl/loaders.rb +12 -0
- data/app/services/spotlight/etl/pipeline.rb +81 -0
- data/app/services/spotlight/etl/solr_loader.rb +96 -0
- data/app/services/spotlight/etl/sources.rb +25 -0
- data/app/services/spotlight/etl/step.rb +82 -0
- data/app/services/spotlight/etl/transforms.rb +64 -0
- data/app/services/spotlight/exhibit_import_export_service.rb +482 -0
- data/app/services/spotlight/validity_checker.rb +5 -5
- data/app/values/custom_field_name.rb +1 -0
- data/app/views/catalog/_save_search.html.erb +1 -1
- data/app/views/spotlight/about_pages/_empty.html.erb +5 -5
- data/app/views/spotlight/browse/_search.html.erb +5 -3
- data/app/views/spotlight/browse/_search_title.html.erb +2 -1
- data/app/views/spotlight/browse/index.html.erb +13 -0
- data/app/views/spotlight/catalog/_document.html.erb +2 -4
- data/app/views/spotlight/catalog/index.iiif_json.jbuilder +22 -0
- data/app/views/spotlight/contacts/_form.html.erb +1 -1
- data/app/views/spotlight/dashboards/_reindexing_activity.html.erb +6 -6
- data/app/views/spotlight/feature_pages/_empty.html.erb +5 -5
- data/app/views/spotlight/featured_images/_form.html.erb +1 -1
- data/app/views/spotlight/featured_images/_upload_form.html.erb +1 -1
- data/app/views/spotlight/home_pages/_empty.html.erb +3 -3
- data/app/views/spotlight/indexing_complete_mailer/documents_indexed.html.erb +9 -0
- data/app/views/spotlight/pages/_form.html.erb +2 -2
- data/app/views/spotlight/searches/_form.html.erb +13 -0
- data/app/views/spotlight/searches/_group.html.erb +27 -0
- data/app/views/spotlight/searches/_search.html.erb +1 -0
- data/app/views/spotlight/searches/index.html.erb +58 -17
- data/app/views/spotlight/shared/_honeypot_field.html.erb +4 -0
- data/app/views/spotlight/shared/_locale_picker.html.erb +1 -1
- data/app/views/spotlight/shared/_report_a_problem.html.erb +7 -10
- data/app/views/spotlight/sir_trevor/blocks/_browse_block.html.erb +1 -0
- data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +45 -0
- data/app/views/spotlight/sir_trevor/blocks/_uploaded_items_block.html.erb +7 -1
- data/app/views/spotlight/translations/_browse_categories.html.erb +29 -3
- data/app/views/spotlight/translations/_general.html.erb +7 -7
- data/app/views/spotlight/translations/_groups.html.erb +34 -0
- data/app/views/spotlight/translations/_import.html.erb +24 -0
- data/app/views/spotlight/translations/_metadata.html.erb +1 -1
- data/app/views/spotlight/translations/_page.html.erb +5 -5
- data/app/views/spotlight/translations/_pages.html.erb +4 -4
- data/app/views/spotlight/translations/_pages_table.html.erb +5 -5
- data/app/views/spotlight/translations/_search_fields.html.erb +3 -3
- data/app/views/spotlight/translations/edit.html.erb +14 -6
- data/app/views/spotlight/translations/show.yaml.yamlbuilder +81 -0
- data/config/i18n-tasks.yml +7 -0
- data/config/locales/spotlight.ar.yml +57 -24
- data/config/locales/spotlight.en.yml +184 -128
- data/config/routes.rb +16 -1
- data/db/migrate/20200403161512_add_subtitle_to_searches.rb +7 -0
- data/db/migrate/20210113092223_create_spotlight_groups.rb +23 -0
- data/db/migrate/20210122082032_create_job_trackers.rb +22 -0
- data/db/migrate/20210126123041_create_events.rb +15 -0
- data/lib/generators/spotlight/install_generator.rb +23 -2
- data/lib/generators/spotlight/scaffold_resource_generator.rb +5 -13
- data/lib/generators/spotlight/templates/config/initializers/sir_trevor_rails.rb +10 -0
- data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +3 -1
- data/lib/spotlight/engine.rb +35 -4
- data/lib/spotlight/upload_field_config.rb +1 -0
- data/lib/spotlight/version.rb +1 -1
- data/spec/controllers/spotlight/about_pages_controller_spec.rb +3 -3
- data/spec/controllers/spotlight/browse_controller_spec.rb +24 -1
- data/spec/controllers/spotlight/catalog_controller_spec.rb +4 -2
- data/spec/controllers/spotlight/contacts_controller_spec.rb +2 -2
- data/spec/controllers/spotlight/feature_pages_controller_spec.rb +11 -0
- data/spec/controllers/spotlight/groups_controller_spec.rb +103 -0
- data/spec/controllers/spotlight/home_pages_controller_spec.rb +2 -2
- data/spec/controllers/spotlight/resources/csv_upload_controller_spec.rb +4 -4
- data/spec/controllers/spotlight/searches_controller_spec.rb +10 -3
- data/spec/controllers/spotlight/translations_controller_spec.rb +53 -2
- data/spec/controllers/spotlight/view_configurations_controller_spec.rb +1 -1
- data/spec/examples.txt +1448 -125
- data/spec/factories/featured_images.rb +4 -0
- data/spec/factories/group.rb +17 -0
- data/spec/factories/job_trackers.rb +9 -0
- data/spec/factories/searches.rb +11 -1
- data/spec/features/add_contacts_spec.rb +1 -1
- data/spec/features/add_items_spec.rb +9 -4
- data/spec/features/browse_category_admin_spec.rb +39 -7
- data/spec/features/browse_category_navigation_spec.rb +44 -0
- data/spec/features/browse_category_spec.rb +2 -2
- data/spec/features/catalog_spec.rb +2 -2
- data/spec/features/create_exhibit_spec.rb +5 -4
- data/spec/features/dashboard_spec.rb +7 -7
- data/spec/features/edit_search_fields_spec.rb +2 -2
- data/spec/features/exhibits/administration_spec.rb +3 -3
- data/spec/features/exhibits/edit_metadata_fields_spec.rb +1 -1
- data/spec/features/exhibits/language_create_edit_spec.rb +3 -3
- data/spec/features/exhibits/translation_editing_spec.rb +57 -8
- data/spec/features/home_page_spec.rb +13 -4
- data/spec/features/item_admin_spec.rb +4 -4
- data/spec/features/javascript/about_page_admin_spec.rb +1 -1
- data/spec/features/javascript/block_controls_spec.rb +3 -1
- data/spec/features/javascript/blocks/browse_group_categories_block_spec.rb +64 -0
- data/spec/features/javascript/blocks/uploaded_items_block_spec.rb +4 -1
- data/spec/features/javascript/browse_group_admin_spec.rb +45 -0
- data/spec/features/javascript/edit_in_place_spec.rb +3 -3
- data/spec/features/javascript/feature_page_admin_spec.rb +1 -1
- data/spec/features/javascript/reindex_monitor_spec.rb +1 -1
- data/spec/features/javascript/search_config_admin_spec.rb +1 -1
- data/spec/features/report_a_problem_spec.rb +6 -5
- data/spec/features/site_users_management_spec.rb +4 -4
- data/spec/helpers/spotlight/crud_link_helpers_spec.rb +3 -3
- data/spec/helpers/spotlight/pages_helper_spec.rb +10 -2
- data/spec/i18n_spec.rb +0 -2
- data/spec/jobs/spotlight/add_uploads_from_csv_spec.rb +13 -1
- data/spec/jobs/spotlight/reindex_exhibit_job_spec.rb +43 -0
- data/spec/jobs/spotlight/reindex_job_spec.rb +30 -59
- data/spec/mailers/spotlight/indexing_complete_mailer_spec.rb +11 -1
- data/spec/models/sir_trevor_rails/blocks/browse_group_categories_block_spec.rb +41 -0
- data/spec/models/solr_document_spec.rb +2 -3
- data/spec/models/spotlight/access_controls_enforcement_search_builder_spec.rb +1 -0
- data/spec/models/spotlight/exhibit_spec.rb +21 -59
- data/spec/models/spotlight/featured_image_spec.rb +27 -0
- data/spec/models/spotlight/group_spec.rb +19 -0
- data/spec/models/spotlight/main_navigation_spec.rb +1 -1
- data/spec/models/spotlight/page_spec.rb +6 -1
- data/spec/models/spotlight/reindex_progress_spec.rb +89 -87
- data/spec/models/spotlight/resource_spec.rb +69 -90
- data/spec/models/spotlight/resources/iiif_harvester_spec.rb +9 -10
- data/spec/models/spotlight/resources/upload_spec.rb +43 -79
- data/spec/models/spotlight/role_spec.rb +3 -3
- data/spec/models/spotlight/search_spec.rb +30 -3
- data/spec/models/spotlight/solr_document_sidecar_spec.rb +1 -0
- data/spec/services/spotlight/etl/context_spec.rb +66 -0
- data/spec/services/spotlight/etl/executor_spec.rb +149 -0
- data/spec/services/spotlight/etl/pipeline_spec.rb +22 -0
- data/spec/services/spotlight/etl/solr_loader_spec.rb +76 -0
- data/spec/services/spotlight/etl/step_spec.rb +70 -0
- data/spec/{serializers/spotlight/exhibit_export_serializer_spec.rb → services/spotlight/exhibit_import_export_service_spec.rb} +168 -23
- data/spec/services/spotlight/iiif_resource_resolver_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -6
- data/spec/support/features/test_features_helpers.rb +15 -0
- data/spec/test_app_templates/Gemfile.extra +1 -3
- data/spec/test_app_templates/catalog_controller.rb +6 -3
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
- data/spec/views/shared/_exhibit_navbar.html.erb_spec.rb +1 -1
- data/spec/views/spotlight/browse/index.html.erb_spec.rb +2 -0
- data/spec/views/spotlight/dashboards/_analytics.html.erb_spec.rb +1 -1
- data/spec/views/spotlight/dashboards/_reindexing_activity.html.erb_spec.rb +28 -25
- data/spec/views/spotlight/metadata_configurations/_metadata_field.html.erb_spec.rb +3 -3
- data/spec/views/spotlight/pages/show.html.erb_spec.rb +1 -0
- data/spec/views/spotlight/search_configurations/_facets.html.erb_spec.rb +1 -1
- data/spec/views/spotlight/search_configurations/_sort.html.erb_spec.rb +7 -8
- data/spec/views/spotlight/translations/_import.html.erb_spec.rb +24 -0
- data/vendor/assets/javascripts/leaflet-iiif.js +46 -21
- data/vendor/assets/javascripts/tiny-slider.js +3218 -0
- data/vendor/assets/stylesheets/tiny-slider.css +1 -0
- metadata +444 -289
- data/app/models/concerns/spotlight/resources/open_graph.rb +0 -36
- data/app/models/spotlight/reindexing_log_entry.rb +0 -42
- data/app/serializers/spotlight/exhibit_export_serializer.rb +0 -205
- data/app/serializers/spotlight/featured_image_representer.rb +0 -29
- data/app/serializers/spotlight/main_navigation_representer.rb +0 -13
- data/app/serializers/spotlight/page_representer.rb +0 -33
- data/app/services/spotlight/resources/iiif_builder.rb +0 -19
- data/app/services/spotlight/solr_document_builder.rb +0 -76
- data/app/services/spotlight/upload_solr_document_builder.rb +0 -57
- data/spec/factories/reindexing_log_entries.rb +0 -54
- data/spec/models/spotlight/reindexing_log_entry_spec.rb +0 -129
- data/spec/models/spotlight/resources/open_graph_spec.rb +0 -65
- data/spec/services/spotlight/solr_document_builder_spec.rb +0 -66
- data/vendor/assets/javascripts/handlebars-v1.3.0.js +0 -2746
@@ -3,9 +3,10 @@
|
|
3
3
|
module Spotlight
|
4
4
|
module Resources
|
5
5
|
##
|
6
|
-
# Shim object for CSV Uploads. see {Spotlight::
|
6
|
+
# Shim object for CSV Uploads. see {Spotlight::AddUploadsFromCsv}
|
7
7
|
class CsvUpload
|
8
8
|
attr_reader :url
|
9
|
+
|
9
10
|
include ActiveModel::Model
|
10
11
|
extend ActiveModel::Translation
|
11
12
|
end
|
@@ -7,7 +7,6 @@ module Spotlight
|
|
7
7
|
# harvest Images from IIIF Manifest and turn them into a Spotlight::Resource
|
8
8
|
# Note: IIIF API : http://iiif.io/api/presentation/2.0
|
9
9
|
class IiifHarvester < Spotlight::Resource
|
10
|
-
self.document_builder_class = Spotlight::Resources::IiifBuilder
|
11
10
|
self.weight = -5000
|
12
11
|
|
13
12
|
validate :valid_url?
|
@@ -16,6 +15,16 @@ module Spotlight
|
|
16
15
|
@iiif_manifests ||= IiifService.parse(url)
|
17
16
|
end
|
18
17
|
|
18
|
+
def self.indexing_pipeline
|
19
|
+
@indexing_pipeline ||= super.dup.tap do |pipeline|
|
20
|
+
pipeline.sources = [Spotlight::Etl::Sources::SourceMethodSource(:iiif_manifests)]
|
21
|
+
|
22
|
+
pipeline.transforms = [
|
23
|
+
->(data, p) { data.merge(p.source.to_solr(exhibit: p.context.resource.exhibit)) }
|
24
|
+
] + pipeline.transforms
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
19
28
|
private
|
20
29
|
|
21
30
|
def valid_url?
|
@@ -6,6 +6,7 @@ module Spotlight
|
|
6
6
|
# A PORO to construct a solr hash for a given IiifManifest
|
7
7
|
class IiifManifest
|
8
8
|
attr_reader :collection
|
9
|
+
|
9
10
|
def initialize(attrs = {})
|
10
11
|
@url = attrs[:url]
|
11
12
|
@manifest = attrs[:manifest]
|
@@ -13,7 +14,9 @@ module Spotlight
|
|
13
14
|
@solr_hash = {}
|
14
15
|
end
|
15
16
|
|
16
|
-
def to_solr
|
17
|
+
def to_solr(exhibit: nil)
|
18
|
+
@exhibit = exhibit if exhibit
|
19
|
+
|
17
20
|
add_document_id
|
18
21
|
add_label
|
19
22
|
add_thumbnail_url
|
@@ -36,6 +39,7 @@ module Spotlight
|
|
36
39
|
private
|
37
40
|
|
38
41
|
attr_reader :url, :manifest, :exhibit, :solr_hash
|
42
|
+
|
39
43
|
delegate :blacklight_config, to: :exhibit
|
40
44
|
|
41
45
|
def add_document_id
|
@@ -67,7 +71,7 @@ module Spotlight
|
|
67
71
|
end
|
68
72
|
|
69
73
|
def add_label
|
70
|
-
return unless title_fields.present? && manifest
|
74
|
+
return unless title_fields.present? && manifest&.label
|
71
75
|
|
72
76
|
Array.wrap(title_fields).each do |field|
|
73
77
|
solr_hash[field] = metadata_class.new(manifest).label
|
@@ -142,7 +146,7 @@ module Spotlight
|
|
142
146
|
end
|
143
147
|
|
144
148
|
def thumbnail_field
|
145
|
-
blacklight_config.index.
|
149
|
+
blacklight_config.index.thumbnail_field
|
146
150
|
end
|
147
151
|
|
148
152
|
def full_image_field
|
@@ -150,11 +154,11 @@ module Spotlight
|
|
150
154
|
end
|
151
155
|
|
152
156
|
def tile_source_field
|
153
|
-
blacklight_config.show.
|
157
|
+
blacklight_config.show.tile_source_field
|
154
158
|
end
|
155
159
|
|
156
160
|
def title_fields
|
157
|
-
Spotlight::Engine.config.iiif_title_fields || blacklight_config.index
|
161
|
+
Spotlight::Engine.config.iiif_title_fields || blacklight_config.index&.title_field
|
158
162
|
end
|
159
163
|
|
160
164
|
def sidecar
|
@@ -185,7 +189,7 @@ module Spotlight
|
|
185
189
|
end
|
186
190
|
|
187
191
|
def label
|
188
|
-
return unless manifest
|
192
|
+
return unless manifest&.label
|
189
193
|
|
190
194
|
Array(json_ld_value(manifest.label)).map { |v| html_sanitize(v) }.first
|
191
195
|
end
|
@@ -195,7 +199,7 @@ module Spotlight
|
|
195
199
|
attr_reader :manifest
|
196
200
|
|
197
201
|
def metadata
|
198
|
-
manifest
|
202
|
+
manifest&.metadata || []
|
199
203
|
end
|
200
204
|
|
201
205
|
def metadata_hash
|
@@ -42,7 +42,7 @@ module Spotlight
|
|
42
42
|
class << self
|
43
43
|
def iiif_response(url)
|
44
44
|
Faraday.get(url).body
|
45
|
-
rescue Faraday::
|
45
|
+
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
|
46
46
|
Rails.logger.warn("HTTP GET for #{url} failed with #{e}")
|
47
47
|
{}.to_json
|
48
48
|
end
|
@@ -5,6 +5,18 @@ module Spotlight
|
|
5
5
|
# Raw solr document uploads
|
6
6
|
class JsonUpload < Spotlight::Resource
|
7
7
|
store :data, accessors: :json
|
8
|
+
|
9
|
+
# The indexing pipeline for JSON uploads copies the data from the stored
|
10
|
+
# `#data` field directly into the indexed document.
|
11
|
+
def self.indexing_pipeline
|
12
|
+
@indexing_pipeline ||= super.dup.tap do |pipeline|
|
13
|
+
pipeline.sources = [Spotlight::Etl::Sources::StoredData]
|
14
|
+
|
15
|
+
pipeline.transforms = [
|
16
|
+
Spotlight::Etl::Transforms::IdentityTransform
|
17
|
+
] + pipeline.transforms
|
18
|
+
end
|
19
|
+
end
|
8
20
|
end
|
9
21
|
end
|
10
22
|
end
|
@@ -10,8 +10,6 @@ module Spotlight
|
|
10
10
|
# we want to do this before reindexing
|
11
11
|
after_create :update_document_sidecar
|
12
12
|
|
13
|
-
self.document_builder_class = UploadSolrDocumentBuilder
|
14
|
-
|
15
13
|
def self.fields(exhibit)
|
16
14
|
@fields ||= {}
|
17
15
|
@fields[exhibit] ||= begin
|
@@ -25,6 +23,15 @@ module Spotlight
|
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
26
|
+
def self.indexing_pipeline
|
27
|
+
@indexing_pipeline ||= super.dup.tap do |pipeline|
|
28
|
+
pipeline.transforms = [
|
29
|
+
->(data, p) { data.merge({ p.context.document_model.unique_key.to_sym => p.source.compound_id }) },
|
30
|
+
Spotlight::Etl::Transforms::SourceMethodTransform(:to_solr)
|
31
|
+
] + pipeline.transforms
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
28
35
|
def compound_id
|
29
36
|
"#{exhibit_id}-#{id}"
|
30
37
|
end
|
@@ -33,6 +40,22 @@ module Spotlight
|
|
33
40
|
@sidecar ||= document_model.new(id: compound_id).sidecar(exhibit)
|
34
41
|
end
|
35
42
|
|
43
|
+
def to_solr
|
44
|
+
return {} unless upload.file_present?
|
45
|
+
|
46
|
+
spotlight_routes = Spotlight::Engine.routes.url_helpers
|
47
|
+
riiif = Riiif::Engine.routes.url_helpers
|
48
|
+
|
49
|
+
dimensions = Riiif::Image.new(upload_id).info
|
50
|
+
|
51
|
+
{
|
52
|
+
spotlight_full_image_width_ssm: dimensions.width,
|
53
|
+
spotlight_full_image_height_ssm: dimensions.height,
|
54
|
+
Spotlight::Engine.config.thumbnail_field => riiif.image_path(upload_id, size: '!400,400'),
|
55
|
+
Spotlight::Engine.config.iiif_manifest_field => spotlight_routes.manifest_exhibit_solr_document_path(exhibit, compound_id)
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
36
59
|
private
|
37
60
|
|
38
61
|
def configured_fields
|
@@ -12,12 +12,17 @@ module Spotlight
|
|
12
12
|
|
13
13
|
self.table_name = 'spotlight_searches'
|
14
14
|
belongs_to :exhibit
|
15
|
+
has_many :group_memberships, class_name: 'Spotlight::GroupMember', as: :member, dependent: :delete_all
|
16
|
+
has_many :groups, through: :group_memberships
|
17
|
+
accepts_nested_attributes_for :group_memberships
|
18
|
+
accepts_nested_attributes_for :groups
|
15
19
|
serialize :query_params, Hash
|
16
20
|
default_scope { order('weight ASC') }
|
17
21
|
scope :published, -> { where(published: true) }
|
22
|
+
scope :unpublished, -> { where(published: [nil, false]) }
|
18
23
|
validates :title, presence: true
|
19
24
|
|
20
|
-
translates :title, :long_description
|
25
|
+
translates :title, :subtitle, :long_description
|
21
26
|
|
22
27
|
has_paper_trail
|
23
28
|
|
@@ -26,6 +31,10 @@ module Spotlight
|
|
26
31
|
accepts_nested_attributes_for :thumbnail, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
|
27
32
|
accepts_nested_attributes_for :masthead, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
|
28
33
|
|
34
|
+
def full_title
|
35
|
+
[title, subtitle.presence].compact.join(' · ')
|
36
|
+
end
|
37
|
+
|
29
38
|
def thumbnail_image_url
|
30
39
|
return unless thumbnail&.iiif_url
|
31
40
|
|
@@ -33,11 +33,7 @@ module Spotlight
|
|
33
33
|
|
34
34
|
# Roll our own polymorphism because our documents are not AREL-able
|
35
35
|
def document
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
def document_type
|
40
|
-
(super.constantize if defined?(super)) || default_document_type
|
36
|
+
document_type_class.new document_type_class.unique_key => document_id
|
41
37
|
end
|
42
38
|
|
43
39
|
def default_document_type
|
@@ -46,6 +42,12 @@ module Spotlight
|
|
46
42
|
|
47
43
|
private
|
48
44
|
|
45
|
+
def document_type_class
|
46
|
+
return default_document_type unless document_type.is_a?(String)
|
47
|
+
|
48
|
+
document_type.constantize
|
49
|
+
end
|
50
|
+
|
49
51
|
def visibility_field
|
50
52
|
blacklight_config.document_model.visibility_field(exhibit)
|
51
53
|
end
|
@@ -72,7 +74,6 @@ module Spotlight
|
|
72
74
|
|
73
75
|
upload_fields.each_with_object({}) do |field, solr_hash|
|
74
76
|
field_name = field.field_name.to_s
|
75
|
-
next unless configured_fields && configured_fields[field_name].present?
|
76
77
|
|
77
78
|
value = configured_fields[field_name]
|
78
79
|
field_data = field.data_to_solr(convert_stored_value_to_solr(value))
|
@@ -83,6 +84,8 @@ module Spotlight
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def upload_fields
|
87
|
+
return [] unless document.uploaded_resource? || resource.is_a?(Spotlight::Resources::Upload)
|
88
|
+
|
86
89
|
Spotlight::Resources::Upload.fields(exhibit)
|
87
90
|
end
|
88
91
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Etl
|
5
|
+
# Contextual information for the ETL pipeline
|
6
|
+
class Context
|
7
|
+
# A hook for downstream applications to report or handle errors using external
|
8
|
+
# systems or services.
|
9
|
+
class_attribute :error_reporter
|
10
|
+
|
11
|
+
attr_reader :arguments, :additional_metadata, :additional_parameters, :logger
|
12
|
+
|
13
|
+
delegate :document_model, to: :resource
|
14
|
+
|
15
|
+
def initialize(*args, additional_metadata: {}, on_error: :log, logger: Rails.logger, **additional_parameters)
|
16
|
+
@arguments = args
|
17
|
+
@additional_metadata = additional_metadata
|
18
|
+
@additional_parameters = additional_parameters
|
19
|
+
@on_error = on_error
|
20
|
+
@logger = logger
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Spotlight::Resource]
|
24
|
+
def resource
|
25
|
+
arguments.first
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String]
|
29
|
+
def unique_key(data)
|
30
|
+
data[document_model&.unique_key&.to_sym || :id]
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# This hook receives any exceptions raised by pipeline steps and handles them
|
35
|
+
# appropriately.
|
36
|
+
def on_error(pipeline, exception, data)
|
37
|
+
error_reporter&.call(pipeline, exception, data)
|
38
|
+
|
39
|
+
case @on_error
|
40
|
+
when :log
|
41
|
+
logger.tagged('ETL') do
|
42
|
+
logger.error("Pipeline error processing resource #{resource.id}: #{exception}")
|
43
|
+
end
|
44
|
+
when :exception
|
45
|
+
raise exception
|
46
|
+
else
|
47
|
+
@on_error&.call(pipeline, exception, data)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Etl
|
5
|
+
# ETL pipeline executor
|
6
|
+
class Executor
|
7
|
+
include ActiveSupport::Benchmarkable
|
8
|
+
|
9
|
+
attr_reader :pipeline, :context, :source, :cache, :logger
|
10
|
+
|
11
|
+
delegate :sources, :pre_processes, :transforms, :post_processes, :loaders, to: :pipeline
|
12
|
+
|
13
|
+
# @param [Spotlight::Etl::Pipeline] pipeline
|
14
|
+
# @param [Spotlight::Etl::Context] context
|
15
|
+
# @param [Hash] cache a shared cache for pipeline steps to store data for the lifetime of the cache
|
16
|
+
def initialize(pipeline, context, cache: nil)
|
17
|
+
@pipeline = pipeline
|
18
|
+
@context = context
|
19
|
+
|
20
|
+
@provided_cache = cache.present?
|
21
|
+
@cache = cache || {}
|
22
|
+
@step_cache = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Execute the ETL pipeline
|
27
|
+
#
|
28
|
+
# @param [Hash] data the initial data structure to pass through to the transform steps
|
29
|
+
# @yield (optionally..) each transformed document after it is transformed but before
|
30
|
+
# it is sent to the loaders
|
31
|
+
def call(data: {}, &block)
|
32
|
+
extract.with_index do |source, index|
|
33
|
+
with_source(source, index) do
|
34
|
+
catch :skip do
|
35
|
+
load(transform(data), &block)
|
36
|
+
end
|
37
|
+
rescue StandardError => e
|
38
|
+
on_error(e, data)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
after_call
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Estimate the number of documents that will be produced by the pipeline
|
47
|
+
#
|
48
|
+
# @return [Number]
|
49
|
+
def estimated_size
|
50
|
+
@estimated_size ||= begin
|
51
|
+
compile_steps(sources).sum { |source| source.call(context).count }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Tagged logger for benchmarks and data flow logging.
|
57
|
+
# NOTE: this is super weird to support Rails 5.2
|
58
|
+
# @private
|
59
|
+
# @yield Logger
|
60
|
+
def with_logger
|
61
|
+
logger = (context&.logger || Rails.logger)
|
62
|
+
logger.tagged(pipeline.class) do
|
63
|
+
logger.tagged("#<#{source.class} id=#{source&.id if source.respond_to?(:id)}>") do
|
64
|
+
@logger = logger
|
65
|
+
yield logger
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# @private
|
72
|
+
# @param [Hash] data
|
73
|
+
# @return [String] a simplified + truncated version of the data hash for debugging
|
74
|
+
def transform_data_for_debugging(data, verbose: $VERBOSE, truncate: 100)
|
75
|
+
return data.inspect.truncate(truncate) unless data.is_a?(Hash)
|
76
|
+
return "id #{context.unique_key(data) || data&.first(5)&.inspect}" unless verbose
|
77
|
+
|
78
|
+
JSON.fast_generate(data).truncate(truncate)
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Propagate exceptions up to the context's error handler.
|
83
|
+
def on_error(exception, data)
|
84
|
+
context.on_error(self, exception, data)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
##
|
90
|
+
# Set the current source
|
91
|
+
# @param [Object] source
|
92
|
+
# @param [Number] index
|
93
|
+
def with_source(source, index)
|
94
|
+
@source = source
|
95
|
+
|
96
|
+
benchmark "Indexing item #{source.inspect.truncate(50)} in resource #{context.resource.id} (#{index} / #{estimated_size})" do
|
97
|
+
yield.tap { @source = nil }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Extract data from sources. The defined sources receive the provided context
|
103
|
+
# and should return an array or other enumerable of sources to pass through
|
104
|
+
# the pipeline.
|
105
|
+
#
|
106
|
+
# @yield [Object]
|
107
|
+
def extract(&block)
|
108
|
+
return to_enum(:extract) { estimated_size } unless block_given?
|
109
|
+
|
110
|
+
compile_steps(sources).each do |source|
|
111
|
+
source.call(context).each do |data|
|
112
|
+
block.call(data)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Transform the source to a document.
|
119
|
+
#
|
120
|
+
# @param [Hash] from the initial seed data used as the input to the initial transforms
|
121
|
+
# @return [Hash] the transformed document
|
122
|
+
def transform(from)
|
123
|
+
compile_steps(pre_processes).each { |step| step.call(from, self) }
|
124
|
+
|
125
|
+
data = compile_steps(transforms).inject(from) { |input, step| step.call(input, self) }
|
126
|
+
|
127
|
+
compile_steps(post_processes).each { |step| step.call(data, self) }
|
128
|
+
|
129
|
+
with_logger do |logger|
|
130
|
+
logger.debug do
|
131
|
+
"Transform output: #{transform_data_for_debugging(data, verbose: true, truncate: 1000)}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
data
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Load a document into a data sink.
|
140
|
+
#
|
141
|
+
# @param [Hash] the fully transformed data
|
142
|
+
# @yield [Hash] the data before it is sent to any loaders
|
143
|
+
def load(data, &block)
|
144
|
+
return unless data
|
145
|
+
|
146
|
+
catch :skip do
|
147
|
+
block&.call(data, self)
|
148
|
+
|
149
|
+
compile_steps(loaders).each do |loader|
|
150
|
+
loader.call(data, self)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# A callback run after transforming data to do any finalizing or cleanup
|
157
|
+
# from the run.
|
158
|
+
def after_call
|
159
|
+
finalize_loaders
|
160
|
+
@cache = {} unless @provided_cache
|
161
|
+
@step_cache = {}
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Loaders may implement a `#finalize` method if they want to perform any work
|
166
|
+
# after all the data is transformed.
|
167
|
+
def finalize_loaders
|
168
|
+
compile_steps(loaders).each do |step|
|
169
|
+
step.finalize(self) if step.respond_to? :finalize
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# DSL convenience utility for writing compact lists of steps; this unrolls
|
175
|
+
# pipeline definitions to contain arrays or hashes, e.g.:
|
176
|
+
# `pipeline.transforms = [step_1: lambda {}, step_2: lambda {}]`
|
177
|
+
#
|
178
|
+
# @return [Enumerable<Spotlight::Etl::Step>]
|
179
|
+
def compile_steps(steps)
|
180
|
+
return to_enum(:compile_steps, steps) unless block_given?
|
181
|
+
|
182
|
+
steps.flatten.each do |step|
|
183
|
+
if step.is_a? Hash
|
184
|
+
step.each do |k, v|
|
185
|
+
yield(@step_cache[k] ||= Spotlight::Etl::Step.new(v, label: k, executor: self))
|
186
|
+
end
|
187
|
+
else
|
188
|
+
yield @step_cache[step] ||= Spotlight::Etl::Step.new(step, executor: self)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|