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
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Etl
|
5
|
+
# ETL pipeline definition
|
6
|
+
class Pipeline
|
7
|
+
include ActiveSupport::Benchmarkable
|
8
|
+
|
9
|
+
attr_reader :context, :source
|
10
|
+
|
11
|
+
# This ETL pipeline system, while somewhat generic, was implemented for Spotlight
|
12
|
+
# to transform Spotlight::Resource instances into Solr documents. The resources
|
13
|
+
# go through a series of steps (sources, transforms, loaders) to produce one or
|
14
|
+
# more documents in the Solr index.
|
15
|
+
#
|
16
|
+
# All of the steps below can be provided as:
|
17
|
+
# - a lambda
|
18
|
+
# - a ruby class (which will be initialized for each pipeline execution)
|
19
|
+
# - or, a hash (of any length) with:
|
20
|
+
# - a key (used only for clarity in logging, particularly useful to label lambdas)
|
21
|
+
# - a value that is one of the valid step types (lambda or ruby class).
|
22
|
+
#
|
23
|
+
# Any of the transform or loader steps can `throw :skip` to skip the current source.
|
24
|
+
#
|
25
|
+
# Any exceptions raised by the pipeline's steps are sent to the context's
|
26
|
+
# error handler by calling `#on_error` on the context object.
|
27
|
+
|
28
|
+
# sources return enumerables that convert from the Spotlight::Etl::Context
|
29
|
+
# to some data structure that the transform steps can handle. The Context is provided
|
30
|
+
# by the implementation when the pipeline is executed.
|
31
|
+
class_attribute :sources, default: []
|
32
|
+
|
33
|
+
# The transform steps (pre-processes, transforms, and post-processes) receive
|
34
|
+
# the current data state and the pipeline. The return value from the transforms
|
35
|
+
# steps replaces the current data state, however the return values for pre- and
|
36
|
+
# post- processing is ignored (although they may mutate the provided data, pipeline, etc).
|
37
|
+
#
|
38
|
+
# Through the pipeline argument, the transform steps can access:
|
39
|
+
# - `context`, the implementation-provided resource
|
40
|
+
# - `source`, the current source instance
|
41
|
+
class_attribute :pre_processes, default: []
|
42
|
+
class_attribute :transforms, default: []
|
43
|
+
class_attribute :post_processes, default: []
|
44
|
+
|
45
|
+
# loaders receive the transformed data and.. do something with it (like load it into Solr)
|
46
|
+
# After all documents are transformed, the loader may also receive `#finalize` to finish any
|
47
|
+
# additional processing.
|
48
|
+
class_attribute :loaders, default: []
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
yield(self) if block_given?
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Execute the ETL pipeline
|
56
|
+
#
|
57
|
+
# @param [Spotlight::Etl::Context] context
|
58
|
+
# @param [Hash] data the initial data structure to pass through to the transform steps
|
59
|
+
# @yield (optioanlly..) each transformed document after it is transformed but before
|
60
|
+
# it is sent to the loaders
|
61
|
+
def call(context, data: {}, cache: nil, &block)
|
62
|
+
executor(context, cache: cache).call(data: data, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Estimate the number of documents that will be produced by the pipeline
|
67
|
+
#
|
68
|
+
# @param [Spotlight::Etl::Context] context
|
69
|
+
# @return [Number]
|
70
|
+
def estimated_size(context)
|
71
|
+
executor(context).estimated_size
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def executor(context, **args)
|
77
|
+
Spotlight::Etl::Executor.new(self, context, **args)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Etl
|
5
|
+
# Solr data loader with a built-in buffer to combine document updates into batches
|
6
|
+
class SolrLoader
|
7
|
+
attr_reader :queue, :batch_size
|
8
|
+
|
9
|
+
delegate :size, to: :queue
|
10
|
+
|
11
|
+
def initialize(batch_size: Spotlight::Engine.config.solr_batch_size, solr_connection: nil)
|
12
|
+
@queue = Queue.new
|
13
|
+
@batch_size = batch_size
|
14
|
+
@blacklight_solr = solr_connection
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(data, pipeline = nil)
|
18
|
+
@queue << data
|
19
|
+
|
20
|
+
write_to_index(pipeline) if @queue.size >= @batch_size
|
21
|
+
end
|
22
|
+
|
23
|
+
def finalize(pipeline = nil)
|
24
|
+
write_to_index(pipeline)
|
25
|
+
|
26
|
+
commit! if pipeline.nil? || pipeline.context.additional_parameters[:commit]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def write_to_index(pipeline)
|
32
|
+
batch = drain_queue
|
33
|
+
|
34
|
+
return unless write? && batch.any?
|
35
|
+
|
36
|
+
send_batch(batch, pipeline)
|
37
|
+
end
|
38
|
+
|
39
|
+
def send_batch(documents, pipeline)
|
40
|
+
blacklight_solr.update params: { commitWithin: 500 },
|
41
|
+
data: documents.to_json,
|
42
|
+
headers: { 'Content-Type' => 'application/json' }
|
43
|
+
rescue StandardError => e
|
44
|
+
logger.warn "Error sending a batch of documents to solr: #{e}"
|
45
|
+
|
46
|
+
documents.each do |doc|
|
47
|
+
send_one(doc, pipeline)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_one(document, pipeline)
|
52
|
+
blacklight_solr.update params: { commitWithin: 500 },
|
53
|
+
data: [document].to_json,
|
54
|
+
headers: { 'Content-Type' => 'application/json' }
|
55
|
+
rescue StandardError => e
|
56
|
+
pipeline&.on_error(self, e, document.to_json)
|
57
|
+
end
|
58
|
+
|
59
|
+
def blacklight_solr
|
60
|
+
@blacklight_solr ||= RSolr.connect(connection_config.merge(adapter: connection_config[:http_adapter]))
|
61
|
+
end
|
62
|
+
|
63
|
+
def connection_config
|
64
|
+
Blacklight.connection_config
|
65
|
+
end
|
66
|
+
|
67
|
+
def drain_queue
|
68
|
+
arr = []
|
69
|
+
|
70
|
+
begin
|
71
|
+
arr << @queue.deq(true) while arr.length < @batch_size && !@queue.empty?
|
72
|
+
rescue ThreadError
|
73
|
+
# @queue throws a ThreadError if it is empty...
|
74
|
+
end
|
75
|
+
|
76
|
+
arr
|
77
|
+
end
|
78
|
+
|
79
|
+
def commit!
|
80
|
+
return unless write?
|
81
|
+
|
82
|
+
blacklight_solr.commit
|
83
|
+
rescue StandardError => e
|
84
|
+
logger.warn "Unable to commit to solr: #{e}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def write?
|
88
|
+
Spotlight::Engine.config.writable_index
|
89
|
+
end
|
90
|
+
|
91
|
+
def logger
|
92
|
+
Rails.logger
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Etl
|
5
|
+
# Basic ETL source implementations
|
6
|
+
module Sources
|
7
|
+
# A simple source that just returns the original resource(s)
|
8
|
+
IdentitySource = lambda do |context|
|
9
|
+
Array.wrap(context.resource)
|
10
|
+
end
|
11
|
+
|
12
|
+
# A transform step that calls a method on the resource to generate a source
|
13
|
+
def self.SourceMethodSource(method) # rubocop:disable Naming/MethodName
|
14
|
+
lambda do |context|
|
15
|
+
context.resource.public_send(method)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# A simple source that retrieves the stored data from a Spotlight::Resource
|
20
|
+
StoredData = lambda do |context, **|
|
21
|
+
Array.wrap(context.resource.data)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Etl
|
5
|
+
# ETL pipeline step
|
6
|
+
class Step
|
7
|
+
attr_reader :definition, :context, :executor
|
8
|
+
|
9
|
+
# @param [Class, Proc] definition the step to run
|
10
|
+
# @param [String] label
|
11
|
+
# @param [Spotlight::Etl::Executor] executor the execution environment
|
12
|
+
def initialize(definition, label: nil, executor: nil)
|
13
|
+
@definition = definition
|
14
|
+
@executor = executor
|
15
|
+
@label = label
|
16
|
+
end
|
17
|
+
|
18
|
+
# rubocop:disable Metrics/MethodLength
|
19
|
+
def call(*args)
|
20
|
+
with_logger do |logger|
|
21
|
+
logger.debug { "Called with #{transform_data_for_debugging(args.first)}" }
|
22
|
+
|
23
|
+
catch :skip do
|
24
|
+
return action.call(*args).tap do |result|
|
25
|
+
logger.debug { " => Returning #{transform_data_for_debugging(result)}" } if $VERBOSE
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
logger.debug ' => Caught skip.'
|
30
|
+
throw :skip
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
with_logger do |logger|
|
34
|
+
logger.error("Caught exception #{e}")
|
35
|
+
end
|
36
|
+
raise(e)
|
37
|
+
end
|
38
|
+
# rubocop:enable Metrics/MethodLength
|
39
|
+
|
40
|
+
def finalize(*args)
|
41
|
+
action.finalize(*args) if action.respond_to? :finalize
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @return [#call]
|
47
|
+
def action
|
48
|
+
case definition
|
49
|
+
when Class
|
50
|
+
# memoize the class' instance for the lifetime of the step
|
51
|
+
@memoized_action ||= definition.new
|
52
|
+
else # Proc, etc
|
53
|
+
definition
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [#to_string]
|
58
|
+
def label
|
59
|
+
@label || definition
|
60
|
+
end
|
61
|
+
|
62
|
+
# NOTE: this is super weird to support Rails 5.2
|
63
|
+
# @return [Logger]
|
64
|
+
def with_logger
|
65
|
+
yield(Rails.logger) && return unless executor
|
66
|
+
|
67
|
+
executor.with_logger do |logger|
|
68
|
+
logger.tagged(label) do
|
69
|
+
yield logger
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# @param [Hash] data
|
76
|
+
# @return [String] a simplified + truncated version of the data hash for debugging
|
77
|
+
def transform_data_for_debugging(data)
|
78
|
+
executor&.transform_data_for_debugging(data) || data.inspect.truncate(100)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Etl
|
5
|
+
# Basic + default transform steps
|
6
|
+
module Transforms
|
7
|
+
# A transform step that "transforms" the source into the data element
|
8
|
+
IdentityTransform = lambda do |data, pipeline|
|
9
|
+
data.merge(pipeline.source)
|
10
|
+
end
|
11
|
+
|
12
|
+
# A transform step that calls a method on the source to generate a document
|
13
|
+
def self.SourceMethodTransform(method) # rubocop:disable Naming/MethodName
|
14
|
+
lambda do |data, pipeline|
|
15
|
+
data.merge(pipeline.source.public_send(method))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# A transform step that throws away blank data
|
20
|
+
RejectBlank = lambda do |data, _|
|
21
|
+
throw :skip if data.blank?
|
22
|
+
data
|
23
|
+
end
|
24
|
+
|
25
|
+
# A trasnform step that ensures data has a unique key attribute
|
26
|
+
RejectMissingUniqueId = lambda do |data, pipeline|
|
27
|
+
id = pipeline.context.unique_key(data)
|
28
|
+
|
29
|
+
throw :skip if id.blank?
|
30
|
+
data
|
31
|
+
end
|
32
|
+
|
33
|
+
# A transform that adds exhibit-specific metadata (like Spotlight sidecar data)
|
34
|
+
# to the document
|
35
|
+
ApplyExhibitMetadata = lambda do |data, pipeline|
|
36
|
+
resource = pipeline.context.resource
|
37
|
+
document_model = pipeline.context.document_model
|
38
|
+
id = pipeline.context.unique_key(data)
|
39
|
+
|
40
|
+
next data unless document_model && id.present?
|
41
|
+
|
42
|
+
exhibit_metadata = document_model.build_for_exhibit(id, resource.exhibit, resource: (resource if resource.persisted?))
|
43
|
+
|
44
|
+
data.reverse_merge(exhibit_metadata.to_solr)
|
45
|
+
end
|
46
|
+
|
47
|
+
# A transform that adds application-specific metadata (like what resource generated the solr document)
|
48
|
+
ApplyApplicationMetadata = lambda do |data, pipeline|
|
49
|
+
resource = pipeline.context.resource
|
50
|
+
document_model = pipeline.context.document_model
|
51
|
+
|
52
|
+
data.reverse_merge(
|
53
|
+
Spotlight::Engine.config.resource_global_id_field => (resource.to_global_id.to_s if resource.persisted?),
|
54
|
+
document_model.resource_type_field => resource.class.to_s.tableize
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
# A transform that adds externally-provided metadata to the document
|
59
|
+
ApplyPipelineMetadata = lambda do |data, pipeline|
|
60
|
+
data.reverse_merge(pipeline.context.additional_metadata)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,482 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'roar/decorator'
|
4
|
+
require 'roar/json'
|
5
|
+
require 'base64'
|
6
|
+
require 'tempfile'
|
7
|
+
|
8
|
+
module Spotlight
|
9
|
+
# Utility service for importing and exporting exhibit data
|
10
|
+
class ExhibitImportExportService
|
11
|
+
class_attribute :serialization_pipeline, default: %i[
|
12
|
+
raw_json
|
13
|
+
add_feature_page_hierarchy
|
14
|
+
add_browse_group_hierarchy
|
15
|
+
add_page_content
|
16
|
+
attach_featured_images
|
17
|
+
attach_attachments
|
18
|
+
]
|
19
|
+
|
20
|
+
attr_reader :exhibit, :include
|
21
|
+
|
22
|
+
def initialize(exhibit, include: Spotlight::Engine.config.exports)
|
23
|
+
@exhibit = exhibit
|
24
|
+
@include = include
|
25
|
+
end
|
26
|
+
|
27
|
+
def from_hash!(hash)
|
28
|
+
hash = hash.deep_symbolize_keys.reverse_merge(
|
29
|
+
main_navigations: {},
|
30
|
+
contact_emails: {},
|
31
|
+
searches: {},
|
32
|
+
about_pages: {},
|
33
|
+
feature_pages: {},
|
34
|
+
contacts: {},
|
35
|
+
custom_fields: {},
|
36
|
+
solr_document_sidecars: {},
|
37
|
+
resources: {},
|
38
|
+
attachments: {},
|
39
|
+
languages: {},
|
40
|
+
translations: {},
|
41
|
+
owned_taggings: {},
|
42
|
+
groups: {}
|
43
|
+
)
|
44
|
+
|
45
|
+
exhibit_attributes = hash.reject { |_k, v| v.is_a?(Array) || v.is_a?(Hash) }
|
46
|
+
exhibit.update(exhibit_attributes.except(:theme))
|
47
|
+
exhibit.theme = exhibit_attributes[:theme] if exhibit.themes.include? exhibit_attributes[:theme]
|
48
|
+
|
49
|
+
deserialize_featured_image(exhibit, :masthead, hash[:masthead]) if hash[:masthead]
|
50
|
+
deserialize_featured_image(exhibit, :thumbnail, hash[:thumbnail]) if hash[:thumbnail]
|
51
|
+
|
52
|
+
exhibit.blacklight_configuration.update hash[:blacklight_configuration].deep_stringify_keys if hash[:blacklight_configuration]
|
53
|
+
|
54
|
+
hash[:main_navigations].each do |attr|
|
55
|
+
ar = exhibit.main_navigations.find_or_initialize_by(nav_type: attr[:nav_type])
|
56
|
+
ar.update(attr)
|
57
|
+
end
|
58
|
+
|
59
|
+
hash[:contact_emails].each do |attr|
|
60
|
+
ar = exhibit.contact_emails.find_or_initialize_by(email: attr[:email])
|
61
|
+
ar.update(attr)
|
62
|
+
end
|
63
|
+
|
64
|
+
hash[:groups].each do |attr|
|
65
|
+
gr = exhibit.groups.find_or_initialize_by(slug: attr[:slug])
|
66
|
+
gr.update(attr)
|
67
|
+
end
|
68
|
+
|
69
|
+
hash[:searches].each do |attr|
|
70
|
+
group_slugs = attr.delete(:group_slugs) || []
|
71
|
+
masthead = attr.delete(:masthead)
|
72
|
+
thumbnail = attr.delete(:thumbnail)
|
73
|
+
|
74
|
+
ar = exhibit.searches.find_or_initialize_by(slug: attr[:slug])
|
75
|
+
ar.update(attr)
|
76
|
+
|
77
|
+
ar.update(groups: exhibit.groups.select { |x| group_slugs.include? x.slug })
|
78
|
+
|
79
|
+
deserialize_featured_image(ar, :masthead, masthead) if masthead
|
80
|
+
deserialize_featured_image(ar, :thumbnail, thumbnail) if thumbnail
|
81
|
+
end
|
82
|
+
|
83
|
+
hash[:about_pages].each do |attr|
|
84
|
+
masthead = attr.delete(:masthead)
|
85
|
+
thumbnail = attr.delete(:thumbnail)
|
86
|
+
translated_pages = attr.delete(:translated_pages) || []
|
87
|
+
|
88
|
+
ar = exhibit.about_pages.find_or_initialize_by(slug: attr[:slug])
|
89
|
+
ar.update(attr)
|
90
|
+
|
91
|
+
deserialize_featured_image(ar, :masthead, masthead) if masthead
|
92
|
+
deserialize_featured_image(ar, :thumbnail, thumbnail) if thumbnail
|
93
|
+
|
94
|
+
translated_pages.each do |tattr|
|
95
|
+
masthead = tattr.delete(:masthead)
|
96
|
+
thumbnail = tattr.delete(:thumbnail)
|
97
|
+
|
98
|
+
tar = ar.translated_page_for(tattr[:locale]) || ar.clone_for_locale(tattr[:locale])
|
99
|
+
tar.update(tattr)
|
100
|
+
|
101
|
+
deserialize_featured_image(ar, :masthead, masthead) if masthead
|
102
|
+
deserialize_featured_image(ar, :thumbnail, thumbnail) if thumbnail
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
hash[:feature_pages].each do |attr|
|
107
|
+
masthead = attr.delete(:masthead)
|
108
|
+
thumbnail = attr.delete(:thumbnail)
|
109
|
+
|
110
|
+
ar = exhibit.feature_pages.find_or_initialize_by(slug: attr[:slug])
|
111
|
+
ar.update(attr.except(:parent_page_slug, :translated_pages))
|
112
|
+
|
113
|
+
deserialize_featured_image(ar, :masthead, masthead) if masthead
|
114
|
+
deserialize_featured_image(ar, :thumbnail, thumbnail) if thumbnail
|
115
|
+
end
|
116
|
+
|
117
|
+
feature_pages = exhibit.feature_pages.index_by(&:slug)
|
118
|
+
hash[:feature_pages].each do |attr|
|
119
|
+
next unless attr[:parent_page_slug]
|
120
|
+
|
121
|
+
feature_pages[attr[:slug]].parent_page_id = feature_pages[attr[:parent_page_slug]].id
|
122
|
+
end
|
123
|
+
|
124
|
+
hash[:feature_pages].each do |attr|
|
125
|
+
ar = exhibit.feature_pages.find_or_initialize_by(slug: attr[:slug])
|
126
|
+
|
127
|
+
(attr[:translated_pages] || []).each do |tattr|
|
128
|
+
masthead = tattr.delete(:masthead)
|
129
|
+
thumbnail = tattr.delete(:thumbnail)
|
130
|
+
|
131
|
+
tar = ar.translated_page_for(tattr[:locale]) || ar.clone_for_locale(tattr[:locale])
|
132
|
+
tar.update(tattr)
|
133
|
+
|
134
|
+
deserialize_featured_image(ar, :masthead, masthead) if masthead
|
135
|
+
deserialize_featured_image(ar, :thumbnail, thumbnail) if thumbnail
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
if hash[:home_page]
|
140
|
+
translated_pages = hash[:home_page].delete(:translated_pages) || []
|
141
|
+
exhibit.home_page.update(hash[:home_page].except(:thumbnail))
|
142
|
+
deserialize_featured_image(exhibit.home_page, :thumbnail, hash[:home_page][:thumbnail]) if hash[:home_page][:thumbnail]
|
143
|
+
|
144
|
+
translated_pages.each do |tattr|
|
145
|
+
masthead = tattr.delete(:masthead)
|
146
|
+
thumbnail = tattr.delete(:thumbnail)
|
147
|
+
|
148
|
+
tar = exhibit.home_page.translated_page_for(tattr[:locale]) || exhibit.home_page.clone_for_locale(tattr[:locale])
|
149
|
+
tar.update(tattr)
|
150
|
+
|
151
|
+
deserialize_featured_image(ar, :masthead, masthead) if masthead
|
152
|
+
deserialize_featured_image(ar, :thumbnail, thumbnail) if thumbnail
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
hash[:contacts].each do |attr|
|
157
|
+
avatar = attr.delete(:avatar)
|
158
|
+
|
159
|
+
ar = exhibit.contacts.find_or_initialize_by(slug: attr[:slug])
|
160
|
+
ar.update(attr)
|
161
|
+
|
162
|
+
deserialize_featured_image(ar, :avatar, avatar) if avatar
|
163
|
+
end
|
164
|
+
|
165
|
+
hash[:custom_fields].each do |attr|
|
166
|
+
ar = exhibit.custom_fields.find_or_initialize_by(slug: attr[:slug])
|
167
|
+
ar.update(attr)
|
168
|
+
end
|
169
|
+
|
170
|
+
hash[:solr_document_sidecars].each do |attr|
|
171
|
+
ar = exhibit.solr_document_sidecars.find_or_initialize_by(document_id: attr[:document_id])
|
172
|
+
ar.update(attr)
|
173
|
+
end
|
174
|
+
|
175
|
+
hash[:resources].each do |attr|
|
176
|
+
upload = attr.delete(:upload)
|
177
|
+
|
178
|
+
ar = exhibit.resources.find_or_initialize_by(type: attr[:type], url: attr[:url])
|
179
|
+
ar.update(attr)
|
180
|
+
|
181
|
+
deserialize_featured_image(ar, :upload, upload) if upload
|
182
|
+
end
|
183
|
+
|
184
|
+
hash[:attachments].each do |attr|
|
185
|
+
file = attr.delete(:file)
|
186
|
+
|
187
|
+
# dedupe by something??
|
188
|
+
ar = exhibit.attachments.build(attr)
|
189
|
+
ar.file = CarrierWave::SanitizedFile.new tempfile: StringIO.new(Base64.decode64(file[:content])),
|
190
|
+
filename: file[:filename],
|
191
|
+
content_type: file[:content_type]
|
192
|
+
end
|
193
|
+
|
194
|
+
hash[:languages].each do |attr|
|
195
|
+
ar = exhibit.languages.find_or_initialize_by(locale: attr[:locale])
|
196
|
+
ar.update(attr)
|
197
|
+
end
|
198
|
+
|
199
|
+
hash[:translations].each do |attr|
|
200
|
+
ar = exhibit.translations.find_or_initialize_by(locale: attr[:locale], key: attr[:key])
|
201
|
+
ar.update(attr)
|
202
|
+
end
|
203
|
+
|
204
|
+
hash[:owned_taggings].each do |attr|
|
205
|
+
tag = ActsAsTaggableOn::Tag.find_or_create_by(name: attr[:tag][:name])
|
206
|
+
exhibit.owned_taggings.build(attr.except(:tag).merge(tag_id: tag.id))
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def deserialize_featured_image(obj, method, data)
|
211
|
+
file = data.delete(:image)
|
212
|
+
image = obj.public_send("build_#{method}")
|
213
|
+
image.update(data)
|
214
|
+
if file
|
215
|
+
image.image = CarrierWave::SanitizedFile.new tempfile: StringIO.new(Base64.decode64(file[:content])),
|
216
|
+
filename: file[:filename],
|
217
|
+
content_type: file[:content_type]
|
218
|
+
# Unset the iiif_tilesource field as the new image should be different, because
|
219
|
+
# the source has been reloaded
|
220
|
+
image.iiif_tilesource = nil
|
221
|
+
end
|
222
|
+
image.save!
|
223
|
+
obj.update(method => image)
|
224
|
+
end
|
225
|
+
|
226
|
+
def as_json(*_args)
|
227
|
+
self.class.serialization_pipeline.inject({}) do |memo, step|
|
228
|
+
method(step).call(memo)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def attach_featured_images(json)
|
235
|
+
json[:masthead] = serialize_featured_image(json[:masthead_id]) if json[:masthead_id]
|
236
|
+
json.delete(:masthead_id)
|
237
|
+
json[:thumbnail] = serialize_featured_image(json[:thumbnail_id]) if json[:thumbnail_id]
|
238
|
+
json.delete(:thumbnail_id)
|
239
|
+
|
240
|
+
(json[:searches] || []).each do |search|
|
241
|
+
search[:masthead] = serialize_featured_image(search[:masthead_id]) if search[:masthead_id]
|
242
|
+
search.delete(:masthead_id)
|
243
|
+
search[:thumbnail] = serialize_featured_image(search[:thumbnail_id]) if search[:thumbnail_id]
|
244
|
+
search.delete(:thumbnail_id)
|
245
|
+
end
|
246
|
+
|
247
|
+
(json[:about_pages] || []).each do |page|
|
248
|
+
page[:masthead] = serialize_featured_image(page[:masthead_id]) if page[:masthead_id]
|
249
|
+
page.delete(:masthead_id)
|
250
|
+
page[:thumbnail] = serialize_featured_image(page[:thumbnail_id]) if page[:thumbnail_id]
|
251
|
+
page.delete(:thumbnail_id)
|
252
|
+
|
253
|
+
(page[:translated_pages] || []).each do |translated_page|
|
254
|
+
translated_page[:masthead] = serialize_featured_image(translated_page[:masthead_id]) if translated_page[:masthead_id]
|
255
|
+
translated_page.delete(:masthead_id)
|
256
|
+
translated_page[:thumbnail] = serialize_featured_image(translated_page[:thumbnail_id]) if translated_page[:thumbnail_id]
|
257
|
+
translated_page.delete(:thumbnail_id)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
(json[:feature_pages] || []).each do |page|
|
262
|
+
page[:masthead] = serialize_featured_image(page[:masthead_id]) if page[:masthead_id]
|
263
|
+
page.delete(:masthead_id)
|
264
|
+
page[:thumbnail] = serialize_featured_image(page[:thumbnail_id]) if page[:thumbnail_id]
|
265
|
+
page.delete(:thumbnail_id)
|
266
|
+
|
267
|
+
(page[:translated_pages] || []).each do |translated_page|
|
268
|
+
translated_page[:masthead] = serialize_featured_image(translated_page[:masthead_id]) if translated_page[:masthead_id]
|
269
|
+
translated_page.delete(:masthead_id)
|
270
|
+
translated_page[:thumbnail] = serialize_featured_image(translated_page[:thumbnail_id]) if translated_page[:thumbnail_id]
|
271
|
+
translated_page.delete(:thumbnail_id)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
if json[:home_page]
|
276
|
+
json[:home_page][:masthead] = serialize_featured_image(json[:home_page][:masthead_id]) if json[:home_page][:masthead_id]
|
277
|
+
json[:home_page].delete(:masthead_id)
|
278
|
+
json[:home_page][:thumbnail] = serialize_featured_image(json[:home_page][:thumbnail_id]) if json[:home_page][:thumbnail_id]
|
279
|
+
json[:home_page].delete(:thumbnail_id)
|
280
|
+
|
281
|
+
(json[:home_page][:translated_pages] || []).each do |translated_page|
|
282
|
+
translated_page[:masthead] = serialize_featured_image(translated_page[:masthead_id]) if translated_page[:masthead_id]
|
283
|
+
translated_page.delete(:masthead_id)
|
284
|
+
translated_page[:thumbnail] = serialize_featured_image(translated_page[:thumbnail_id]) if translated_page[:thumbnail_id]
|
285
|
+
translated_page.delete(:thumbnail_id)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
(json[:contacts] || []).each do |page|
|
290
|
+
page[:avatar] = serialize_featured_image(page[:avatar_id]) if page[:avatar_id]
|
291
|
+
page.delete(:avatar_id)
|
292
|
+
end
|
293
|
+
|
294
|
+
(json[:resources] || []).each do |page|
|
295
|
+
page[:upload] = serialize_featured_image(page[:upload_id]) if page[:upload_id]
|
296
|
+
page.delete(:upload_id)
|
297
|
+
end
|
298
|
+
|
299
|
+
json
|
300
|
+
end
|
301
|
+
|
302
|
+
def serialize_featured_image(id)
|
303
|
+
image = Spotlight::FeaturedImage.find(id)
|
304
|
+
file = image.image.file
|
305
|
+
if file
|
306
|
+
img = {
|
307
|
+
image: {
|
308
|
+
filename: file.filename, content_type: file.content_type, content: Base64.encode64(file.read)
|
309
|
+
}
|
310
|
+
}
|
311
|
+
end
|
312
|
+
|
313
|
+
image.as_json(except: %i[id image]).merge(img || {}).deep_symbolize_keys
|
314
|
+
end
|
315
|
+
|
316
|
+
def attach_attachments(json)
|
317
|
+
return json unless json[:attachments]
|
318
|
+
|
319
|
+
json[:attachments].each do |attachment|
|
320
|
+
a = exhibit.attachments.find(attachment[:id])
|
321
|
+
file = a.file.file
|
322
|
+
|
323
|
+
attachment[:file] = {
|
324
|
+
filename: file.filename,
|
325
|
+
content_type: file.content_type,
|
326
|
+
content: Base64.encode64(file.read)
|
327
|
+
}
|
328
|
+
|
329
|
+
attachment.delete(:id)
|
330
|
+
end
|
331
|
+
|
332
|
+
json
|
333
|
+
end
|
334
|
+
|
335
|
+
def add_feature_page_hierarchy(json)
|
336
|
+
return json unless json[:feature_pages]
|
337
|
+
|
338
|
+
page_id_map = json[:feature_pages].map { |x| [x[:id], x[:slug]] }.to_h
|
339
|
+
|
340
|
+
json[:feature_pages].each do |page|
|
341
|
+
page.delete(:id)
|
342
|
+
next unless page[:parent_page_id]
|
343
|
+
|
344
|
+
page[:parent_page_slug] = page_id_map[page[:parent_page_id]]
|
345
|
+
page.delete(:parent_page_id)
|
346
|
+
end
|
347
|
+
|
348
|
+
json
|
349
|
+
end
|
350
|
+
|
351
|
+
def add_browse_group_hierarchy(json)
|
352
|
+
return json unless json[:groups] && json[:searches]
|
353
|
+
|
354
|
+
json[:searches].each do |attr|
|
355
|
+
search = exhibit.searches.find_by(slug: attr[:slug])
|
356
|
+
|
357
|
+
attr[:group_slugs] = search.groups.pluck(:slug)
|
358
|
+
end
|
359
|
+
|
360
|
+
json
|
361
|
+
end
|
362
|
+
|
363
|
+
def add_page_content(json)
|
364
|
+
(json[:feature_pages] || []).each do |page|
|
365
|
+
p = exhibit.feature_pages.find_by(slug: page[:slug])
|
366
|
+
page[:content] = p.read_attribute(:content)
|
367
|
+
(page[:translated_pages]).each do |translated_page|
|
368
|
+
translated_page[:content] = p.translated_page_for(translated_page[:locale]).read_attribute(:content)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
(json[:about_pages] || []).each do |page|
|
373
|
+
p = exhibit.about_pages.find_by(slug: page[:slug])
|
374
|
+
page[:content] = p.read_attribute(:content)
|
375
|
+
(page[:translated_pages]).each do |translated_page|
|
376
|
+
translated_page[:content] = p.translated_page_for(translated_page[:locale]).read_attribute(:content)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
json[:home_page][:content] = exhibit.home_page.read_attribute(:content) if json[:home_page]
|
381
|
+
(json.dig(:home_page, :translated_pages) || []).each do |translated_page|
|
382
|
+
translated_page[:content] = exhibit.home_page.translated_page_for(translated_page[:locale]).read_attribute(:content)
|
383
|
+
end
|
384
|
+
json
|
385
|
+
end
|
386
|
+
|
387
|
+
def raw_json(_input = nil)
|
388
|
+
exhibit.as_json(
|
389
|
+
{
|
390
|
+
except: %i[id slug site_id],
|
391
|
+
include: {}.merge(
|
392
|
+
if_include?(:config,
|
393
|
+
main_navigations: {
|
394
|
+
except: %i[id exhibit_id]
|
395
|
+
},
|
396
|
+
contact_emails: {
|
397
|
+
except: %i[id exhibit_id confirmation_token]
|
398
|
+
},
|
399
|
+
languages: {
|
400
|
+
except: %i[id exhibit_id]
|
401
|
+
},
|
402
|
+
translations: {
|
403
|
+
only: %i[locale key value interpolations is_proc]
|
404
|
+
})
|
405
|
+
).merge(
|
406
|
+
if_include?(:pages,
|
407
|
+
searches: { # thumbnail
|
408
|
+
except: %i[id scope exhibit_id]
|
409
|
+
},
|
410
|
+
groups: {
|
411
|
+
except: %i[id exhibit_id]
|
412
|
+
},
|
413
|
+
about_pages: { # thumbnail
|
414
|
+
except: %i[id scope exhibit_id parent_page_id content],
|
415
|
+
include: {
|
416
|
+
translated_pages: {
|
417
|
+
except: %i[id scope exhibit_id parent_page_id default_locale_page_id content]
|
418
|
+
}
|
419
|
+
}
|
420
|
+
},
|
421
|
+
home_page: { # thumbnail
|
422
|
+
except: %i[id slug scope exhibit_id parent_page_id content],
|
423
|
+
include: {
|
424
|
+
translated_pages: {
|
425
|
+
except: %i[id scope exhibit_id parent_page_id default_locale_page_id content]
|
426
|
+
}
|
427
|
+
}
|
428
|
+
},
|
429
|
+
feature_pages: { # thumbnail
|
430
|
+
except: %i[scope exhibit_id content],
|
431
|
+
include: {
|
432
|
+
translated_pages: {
|
433
|
+
except: %i[id scope exhibit_id parent_page_id default_locale_page_id content]
|
434
|
+
}
|
435
|
+
}
|
436
|
+
},
|
437
|
+
contacts: {
|
438
|
+
except: %i[id exhibit_id]
|
439
|
+
})
|
440
|
+
).merge(
|
441
|
+
if_include?(:blacklight_configuration,
|
442
|
+
blacklight_configuration: {
|
443
|
+
except: %i[id exhibit_id]
|
444
|
+
},
|
445
|
+
# blacklight_configuration
|
446
|
+
custom_fields: {
|
447
|
+
except: %i[id exhibit_id]
|
448
|
+
})
|
449
|
+
).merge(
|
450
|
+
if_include?(:resources,
|
451
|
+
# resources
|
452
|
+
solr_document_sidecars: {
|
453
|
+
except: %i[id exhibit_id]
|
454
|
+
},
|
455
|
+
owned_taggings: {
|
456
|
+
only: %i[taggable_id taggable_type context],
|
457
|
+
include: {
|
458
|
+
tag: {
|
459
|
+
only: [:name]
|
460
|
+
}
|
461
|
+
}
|
462
|
+
},
|
463
|
+
resources: { # upload
|
464
|
+
except: %i[id exhibit_id],
|
465
|
+
methods: :type
|
466
|
+
})
|
467
|
+
).merge(
|
468
|
+
if_include?(:attachments,
|
469
|
+
# attachments
|
470
|
+
attachments: { # file
|
471
|
+
except: %i[exhibit_id]
|
472
|
+
})
|
473
|
+
)
|
474
|
+
}.merge(include[:config] ? {} : { only: %i[does_not_exist] })
|
475
|
+
).deep_symbolize_keys
|
476
|
+
end
|
477
|
+
|
478
|
+
def if_include?(config, res)
|
479
|
+
include[config] ? res : {}
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|