arclight 0.0.1 → 0.1.0
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/.eslintrc +12 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +66 -0
- data/.solr_wrapper +5 -0
- data/.travis.yml +30 -2
- data/CONTRIBUTING.md +43 -0
- data/Gemfile +36 -0
- data/LICENSE.txt +1 -0
- data/README.md +85 -12
- data/Rakefile +14 -3
- data/app/assets/images/blacklight/compact.svg +25 -0
- data/app/assets/images/blacklight/logo.png +0 -0
- data/app/assets/javascripts/arclight/arclight.js +9 -0
- data/app/assets/javascripts/arclight/collection_context.js +18 -0
- data/app/assets/javascripts/arclight/collection_navigation.js +114 -0
- data/app/assets/javascripts/arclight/collection_scrollspy.js +6 -0
- data/app/assets/javascripts/arclight/component_ancestors.js +56 -0
- data/app/assets/javascripts/arclight/oembed_viewer.js +39 -0
- data/app/assets/javascripts/arclight/truncator.js.erb +23 -0
- data/app/assets/stylesheets/arclight/application.scss +13 -0
- data/app/assets/stylesheets/arclight/bootstrap_overrides.scss +3 -0
- data/app/assets/stylesheets/arclight/modules/collection_search.scss +3 -0
- data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +174 -0
- data/app/assets/stylesheets/arclight/modules/highlights.scss +10 -0
- data/app/assets/stylesheets/arclight/modules/layout.scss +33 -0
- data/app/assets/stylesheets/arclight/modules/mastheads.scss +60 -0
- data/app/assets/stylesheets/arclight/modules/repositories.scss +29 -0
- data/app/assets/stylesheets/arclight/modules/repository_card.scss +54 -0
- data/app/assets/stylesheets/arclight/modules/search_results.scss +75 -0
- data/app/assets/stylesheets/arclight/modules/show_collection.scss +78 -0
- data/app/assets/stylesheets/arclight/modules/sidebar.scss +16 -0
- data/app/assets/stylesheets/arclight/variables.scss +2 -0
- data/app/controllers/arclight/repositories_controller.rb +40 -0
- data/app/controllers/concerns/arclight/field_config_helpers.rb +86 -0
- data/app/factories/blacklight_field_configuration_factory.rb +29 -0
- data/app/helpers/arclight_helper.rb +173 -0
- data/app/models/arclight/parent.rb +24 -0
- data/app/models/arclight/parents.rb +34 -0
- data/app/models/arclight/requests/google_form.rb +44 -0
- data/app/models/concerns/arclight/catalog.rb +22 -0
- data/app/models/concerns/arclight/search_behavior.rb +46 -0
- data/app/models/concerns/arclight/solr_document.rb +131 -0
- data/app/presenters/arclight/index_presenter.rb +10 -0
- data/app/presenters/arclight/show_presenter.rb +27 -0
- data/app/views/arclight/.keep +0 -0
- data/app/views/arclight/repositories/_in_person_repository.html.erb +19 -0
- data/app/views/arclight/repositories/_repository.html.erb +62 -0
- data/app/views/arclight/repositories/index.html.erb +4 -0
- data/app/views/arclight/repositories/show.html.erb +38 -0
- data/app/views/arclight/requests/_google_form.html.erb +11 -0
- data/app/views/arclight/viewers/_oembed.html.erb +7 -0
- data/app/views/catalog/_arclight_document_index_header.html.erb +13 -0
- data/app/views/catalog/_arclight_document_index_header_hierarchy_default.html.erb +0 -0
- data/app/views/catalog/_arclight_document_index_header_online_contents_default.html.erb +0 -0
- data/app/views/catalog/_arclight_document_show_header.html.erb +15 -0
- data/app/views/catalog/_arclight_document_show_header_collection.html.erb +12 -0
- data/app/views/catalog/_arclight_index_compact_default.html.erb +15 -0
- data/app/views/catalog/_arclight_online_content_indicator.html.erb +5 -0
- data/app/views/catalog/_arclight_rangelimit.html.erb +24 -0
- data/app/views/catalog/_arclight_viewer_default.html.erb +1 -0
- data/app/views/catalog/_collection_contents.html.erb +12 -0
- data/app/views/catalog/_collection_count.html.erb +7 -0
- data/app/views/catalog/_collection_downloads.html.erb +17 -0
- data/app/views/catalog/_collection_online_contents.html.erb +17 -0
- data/app/views/catalog/_collection_overview.html.erb +7 -0
- data/app/views/catalog/_component_overview.html.erb +46 -0
- data/app/views/catalog/_context_card.html.erb +27 -0
- data/app/views/catalog/_context_sidebar.html.erb +8 -0
- data/app/views/catalog/_custom_metadata.html.erb +16 -0
- data/app/views/catalog/_home.html.erb +1 -0
- data/app/views/catalog/_index_breadcrumb_default.html.erb +3 -0
- data/app/views/catalog/_index_default.html.erb +17 -0
- data/app/views/catalog/_index_header.html.erb +7 -0
- data/app/views/catalog/_index_header_hierarchy_default.html.erb +42 -0
- data/app/views/catalog/_index_header_online_contents_default.html.erb +1 -0
- data/app/views/catalog/_index_hierarchy_default.html.erb +28 -0
- data/app/views/catalog/_index_online_contents_default.html.erb +6 -0
- data/app/views/catalog/_results_histogram.html.erb +10 -0
- data/app/views/catalog/_search_results.html.erb +31 -0
- data/app/views/catalog/_search_results_repository.html.erb +6 -0
- data/app/views/catalog/_search_within_form.html.erb +16 -0
- data/app/views/catalog/_show_breadcrumbs_default.html.erb +7 -0
- data/app/views/catalog/_show_collection.html.erb +40 -0
- data/app/views/catalog/_show_component_sidebar.html.erb +12 -0
- data/app/views/catalog/_show_default.html.erb +32 -0
- data/app/views/catalog/_show_header.html.erb +5 -0
- data/app/views/catalog/_show_sidebar.html.erb +30 -0
- data/app/views/catalog/index.html.erb +8 -0
- data/app/views/layouts/catalog_result.html.erb +7 -0
- data/app/views/shared/_breadcrumbs.html.erb +15 -0
- data/app/views/shared/_context_sidebar.html.erb +8 -0
- data/app/views/shared/_header_navbar.html.erb +54 -0
- data/app/views/shared/_main_menu_links.html.erb +6 -0
- data/arclight.gemspec +17 -4
- data/bin/rails +13 -0
- data/config/locales/arclight.en.yml +65 -0
- data/config/routes.rb +6 -0
- data/lib/arclight.rb +5 -0
- data/lib/arclight/custom_component.rb +98 -0
- data/lib/arclight/custom_document.rb +93 -0
- data/lib/arclight/digital_object.rb +26 -0
- data/lib/arclight/engine.rb +55 -0
- data/lib/arclight/exceptions.rb +18 -0
- data/lib/arclight/indexer.rb +9 -0
- data/lib/arclight/normalized_date.rb +45 -0
- data/lib/arclight/normalized_id.rb +25 -0
- data/lib/arclight/normalized_title.rb +30 -0
- data/lib/arclight/repository.rb +91 -0
- data/lib/arclight/shared_indexing_behavior.rb +97 -0
- data/lib/arclight/shared_terminology_behavior.rb +65 -0
- data/lib/arclight/solr_ead_indexer_ext.rb +159 -0
- data/lib/arclight/version.rb +3 -1
- data/lib/arclight/viewer.rb +45 -0
- data/lib/arclight/viewers/oembed.rb +56 -0
- data/lib/arclight/year_range.rb +102 -0
- data/lib/generators/arclight/install_generator.rb +63 -0
- data/lib/generators/arclight/templates/arclight.js +2 -0
- data/lib/generators/arclight/templates/arclight.scss +3 -0
- data/lib/generators/arclight/templates/catalog_controller.rb +347 -0
- data/lib/generators/arclight/templates/config/downloads.yml +13 -0
- data/lib/generators/arclight/templates/config/repositories.yml +42 -0
- data/lib/generators/arclight/update_generator.rb +22 -0
- data/lib/tasks/index.rake +87 -0
- data/package.json +24 -0
- data/solr/conf/_rest_managed.json +3 -0
- data/solr/conf/admin-extra.html +31 -0
- data/solr/conf/elevate.xml +36 -0
- data/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
- data/solr/conf/protwords.txt +21 -0
- data/solr/conf/schema.xml +631 -0
- data/solr/conf/scripts.conf +24 -0
- data/solr/conf/solrconfig.xml +393 -0
- data/solr/conf/spellings.txt +2 -0
- data/solr/conf/stopwords.txt +58 -0
- data/solr/conf/stopwords_en.txt +58 -0
- data/solr/conf/synonyms.txt +31 -0
- data/solr/conf/xslt/example.xsl +132 -0
- data/solr/conf/xslt/example_atom.xsl +67 -0
- data/solr/conf/xslt/example_rss.xsl +66 -0
- data/solr/conf/xslt/luke.xsl +337 -0
- data/tasks/arclight.rake +68 -0
- data/template.rb +15 -0
- data/vendor/assets/javascripts/responsiveTruncator.js +69 -0
- data/vendor/assets/javascripts/stickyfill.js +480 -0
- metadata +301 -6
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arclight
|
|
4
|
+
##
|
|
5
|
+
# A utility class to normalize dates, typically by joining inclusive and bulk dates
|
|
6
|
+
# e.g., "1990-2000, bulk 1990-1999"
|
|
7
|
+
# @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date
|
|
8
|
+
class NormalizedDate
|
|
9
|
+
# @param [String | Array<String>] `inclusive` from the `unitdate`
|
|
10
|
+
# @param [String] `bulk` from the `unitdate`
|
|
11
|
+
# @param [String] `other` from the `unitdate` when type is not specified
|
|
12
|
+
def initialize(inclusive, bulk = nil, other = nil)
|
|
13
|
+
if inclusive.is_a? Array # of YYYY-YYYY for ranges
|
|
14
|
+
@inclusive = YearRange.new(inclusive.include?('/') ? inclusive : inclusive.map { |v| v.tr('-', '/') }).to_s
|
|
15
|
+
elsif inclusive.present?
|
|
16
|
+
@inclusive = inclusive.strip
|
|
17
|
+
end
|
|
18
|
+
@bulk = bulk.strip if bulk.present?
|
|
19
|
+
@other = other.strip if other.present?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [String] the normalized title/date
|
|
23
|
+
def to_s
|
|
24
|
+
normalize
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :inclusive, :bulk, :other
|
|
30
|
+
|
|
31
|
+
# @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date for rules
|
|
32
|
+
def normalize
|
|
33
|
+
if inclusive.present?
|
|
34
|
+
result = inclusive.to_s
|
|
35
|
+
result << ", bulk #{bulk}" if bulk.present?
|
|
36
|
+
elsif other.present?
|
|
37
|
+
result = other.to_s
|
|
38
|
+
else
|
|
39
|
+
result = nil
|
|
40
|
+
end
|
|
41
|
+
return if result.blank?
|
|
42
|
+
result.strip
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arclight
|
|
4
|
+
##
|
|
5
|
+
# A simple utility class to normalize identifiers
|
|
6
|
+
# to be used around the application for linking
|
|
7
|
+
class NormalizedId
|
|
8
|
+
def initialize(id)
|
|
9
|
+
@id = id
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
normalize
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
attr_reader :id
|
|
19
|
+
|
|
20
|
+
def normalize
|
|
21
|
+
raise Arclight::Exceptions::IDNotFound if id.blank?
|
|
22
|
+
id.strip.tr('.', '-')
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arclight
|
|
4
|
+
##
|
|
5
|
+
# A utility class to normalize titles, typically by joining
|
|
6
|
+
# the title and date, e.g., "My Title, 1990-2000"
|
|
7
|
+
class NormalizedTitle
|
|
8
|
+
# @param [String] `title` from the `unittitle`
|
|
9
|
+
# @param [String] `date` from the `unitdate`
|
|
10
|
+
def initialize(title, date = nil)
|
|
11
|
+
@title = title.gsub(/\s*,\s*$/, '').strip if title.present?
|
|
12
|
+
@date = date.strip if date.present?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [String] the normalized title/date
|
|
16
|
+
def to_s
|
|
17
|
+
normalize
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
attr_reader :title, :date, :default
|
|
23
|
+
|
|
24
|
+
def normalize
|
|
25
|
+
result = [title, date].compact.join(', ')
|
|
26
|
+
raise Arclight::Exceptions::TitleNotFound if result.blank?
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arclight
|
|
4
|
+
#
|
|
5
|
+
# Static information about a given repository identified by a unique `slug`
|
|
6
|
+
#
|
|
7
|
+
class Repository
|
|
8
|
+
include ActiveModel::Conversion # for to_partial_path
|
|
9
|
+
|
|
10
|
+
FIELDS = %i[name
|
|
11
|
+
description
|
|
12
|
+
visit_note
|
|
13
|
+
building
|
|
14
|
+
address1
|
|
15
|
+
address2
|
|
16
|
+
city
|
|
17
|
+
state
|
|
18
|
+
zip
|
|
19
|
+
country
|
|
20
|
+
phone
|
|
21
|
+
contact_info
|
|
22
|
+
thumbnail_url
|
|
23
|
+
google_request_url
|
|
24
|
+
google_request_mappings
|
|
25
|
+
collection_count].freeze
|
|
26
|
+
|
|
27
|
+
attr_accessor :slug, *FIELDS
|
|
28
|
+
|
|
29
|
+
# @param [String] `slug` the unique identifier for the repository
|
|
30
|
+
# @param [Hash] `data`
|
|
31
|
+
def initialize(slug, data = {})
|
|
32
|
+
@slug = slug
|
|
33
|
+
FIELDS.each do |field|
|
|
34
|
+
value = data[field.to_s]
|
|
35
|
+
send("#{field}=", value) if value.present?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [String] handles the formatting of "city, state zip, country"
|
|
40
|
+
def city_state_zip_country
|
|
41
|
+
state_zip = state
|
|
42
|
+
state_zip += " #{zip}" if zip
|
|
43
|
+
[city, state_zip, country].compact.join(', ')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Load repository information from a YAML file
|
|
47
|
+
#
|
|
48
|
+
# @param [String] `filename`
|
|
49
|
+
# @return [Hash<Slug,Repository>]
|
|
50
|
+
def self.from_yaml(file)
|
|
51
|
+
repos = {}
|
|
52
|
+
data = YAML.safe_load(File.read(file))
|
|
53
|
+
data.keys.each do |slug|
|
|
54
|
+
repos[slug] = new(slug, data[slug])
|
|
55
|
+
end
|
|
56
|
+
repos
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Mimics ActiveRecord's `all` behavior
|
|
60
|
+
#
|
|
61
|
+
# @return [Array<Repository>]
|
|
62
|
+
def self.all
|
|
63
|
+
from_yaml(ENV['REPOSITORY_FILE'] || 'config/repositories.yml').values
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Mimics ActiveRecord dynamic `find_by` behavior for the slug or name
|
|
67
|
+
#
|
|
68
|
+
# @param [String] `slug` or `name`
|
|
69
|
+
# @return [Repository]
|
|
70
|
+
def self.find_by(slug: nil, name: nil)
|
|
71
|
+
if slug
|
|
72
|
+
all.find { |repo| repo.slug == slug }
|
|
73
|
+
elsif name
|
|
74
|
+
all.find { |repo| repo.name == name }
|
|
75
|
+
else
|
|
76
|
+
raise ArgumentError, 'Requires either slug or name parameters to find_by'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Mimics ActiveRecord dynamic `find_by!` behavior for the slug or name
|
|
81
|
+
#
|
|
82
|
+
# @param [String] `slug` or `name` -- same as `find_by`
|
|
83
|
+
# @return [Repository]
|
|
84
|
+
# @raise [ActiveRecord::RecordNotFound] if cannot find repository
|
|
85
|
+
def self.find_by!(*args)
|
|
86
|
+
repository = find_by(*args)
|
|
87
|
+
raise ActiveRecord::RecordNotFound if repository.blank?
|
|
88
|
+
repository
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arclight
|
|
4
|
+
##
|
|
5
|
+
# A mixin intended to share indexing behavior between
|
|
6
|
+
# the CustomDocument and CustomComponent classes
|
|
7
|
+
module SharedIndexingBehavior
|
|
8
|
+
# @see http://eadiva.com/2/unitdate/
|
|
9
|
+
# @return [YearRange] all of the years between the given years
|
|
10
|
+
def unitdate_for_range
|
|
11
|
+
range = YearRange.new
|
|
12
|
+
return range if normal_unit_dates.blank?
|
|
13
|
+
range << range.parse_ranges(normal_unit_dates)
|
|
14
|
+
range
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def subjects_array(elements, parent:)
|
|
18
|
+
xpath_elements = elements.map { |el| "local-name()='#{el}'" }.join(' or ')
|
|
19
|
+
subjects = search("//#{parent}/controlaccess/*[#{xpath_elements}]").to_a
|
|
20
|
+
clean_facets_array(subjects.flatten.map(&:text))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def names_array(elements, parent:)
|
|
24
|
+
xpath_elements = elements.map { |el| "local-name()='#{el}'" }.join(' or ')
|
|
25
|
+
names = search("//#{parent}/controlaccess/*[#{xpath_elements}]").to_a
|
|
26
|
+
clean_facets_array(names.flatten.map(&:text))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Return a cleaned array of facets without marc subfields
|
|
30
|
+
#
|
|
31
|
+
# E.g. clean_facets_array(
|
|
32
|
+
# ['FacetValue1 |z FacetValue2','FacetValue3']
|
|
33
|
+
# ) => ['FacetValue1 -- FacetValue2', 'FacetValue3']
|
|
34
|
+
def clean_facets_array(facets_array)
|
|
35
|
+
Array(facets_array).map { |text| fix_subfield_demarcators(text) }.compact.uniq
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Replace MARC style subfield demarcators
|
|
39
|
+
#
|
|
40
|
+
# Usage: fix_subfield_demarcators("Subject 1 |z Sub-Subject 2") => "Subject 1 -- Sub-Subject 2"
|
|
41
|
+
def fix_subfield_demarcators(value)
|
|
42
|
+
value.gsub(/\|\w{1}/, '--')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Wrap OM's find_by_xpath for convenience
|
|
46
|
+
def search(path)
|
|
47
|
+
find_by_xpath(path) # rubocop:disable DynamicFindBy
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# If a repository slug is provided via an environment variable `REPOSITORY_ID`,
|
|
51
|
+
# then use that to lookup the name rather than the parsed out name from the EAD
|
|
52
|
+
# @param [String] `repository` the default repository name
|
|
53
|
+
def repository_as_configured(repository)
|
|
54
|
+
slug = ENV['REPOSITORY_ID']
|
|
55
|
+
if slug.present?
|
|
56
|
+
begin
|
|
57
|
+
Arclight::Repository.find_by(slug: slug).name
|
|
58
|
+
rescue => e
|
|
59
|
+
raise "The repository slug '#{slug}' was given but it is not found in the Repository configuration data: #{e}"
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
repository
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def add_digital_content(prefix:, solr_doc:)
|
|
67
|
+
dao = ng_xml.xpath("#{prefix}/dao").to_a
|
|
68
|
+
return if dao.blank?
|
|
69
|
+
field_name = Solrizer.solr_name('digital_objects', :displayable)
|
|
70
|
+
solr_doc[field_name] = digital_objects(dao)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def digital_objects(objects)
|
|
74
|
+
objects.map do |dao|
|
|
75
|
+
label = dao.attributes['title'].try(:value) || dao.xpath('daodesc/p').try(:text)
|
|
76
|
+
href = (dao.attributes['href'] || dao.attributes['xlink:href']).try(:value)
|
|
77
|
+
Arclight::DigitalObject.new(label: label, href: href).to_json
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def add_date_ranges(solr_doc)
|
|
82
|
+
Solrizer.insert_field(solr_doc, 'date_range', unitdate_for_range.years, :facetable)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def add_normalized_title(solr_doc)
|
|
86
|
+
dates = Arclight::NormalizedDate.new(unitdate_inclusive.first, unitdate_bulk.first, unitdate_other.first).to_s
|
|
87
|
+
title = Arclight::NormalizedTitle.new(solr_doc['title_ssm'].try(:first), dates).to_s
|
|
88
|
+
solr_doc['normalized_title_ssm'] = [title]
|
|
89
|
+
solr_doc['normalized_date_ssm'] = [dates]
|
|
90
|
+
title
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def online_content?
|
|
94
|
+
search('//dao[@href]').present?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arclight
|
|
4
|
+
##
|
|
5
|
+
# An extendable mixin intended to share terminology behavior between
|
|
6
|
+
# the CustomDocument and CustomComponent classes
|
|
7
|
+
module SharedTerminologyBehavior
|
|
8
|
+
def add_unitid(t, prefix)
|
|
9
|
+
t.unitid(path: prefix + 'did/unitid', index_as: %i[displayable searchable])
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_extent(t, prefix)
|
|
13
|
+
t.extent(path: prefix + 'did/physdesc/extent', index_as: %i[displayable searchable])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# date indexing
|
|
17
|
+
def add_dates(t, prefix)
|
|
18
|
+
t.normal_unit_dates(path: prefix + 'did/unitdate/@normal')
|
|
19
|
+
t.unitdate_bulk(path: prefix + 'did/unitdate[@type=\'bulk\']', index_as: %i[displayable])
|
|
20
|
+
t.unitdate_inclusive(path: prefix + 'did/unitdate[@type=\'inclusive\']', index_as: %i[displayable])
|
|
21
|
+
t.unitdate_other(path: prefix + 'did/unitdate[not(@type)]', index_as: %i[displayable])
|
|
22
|
+
t.unitdate(path: prefix + 'did/unitdate', index_as: %i[displayable])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_searchable_notes(t, prefix) # rubocop: disable Metrics/MethodLength
|
|
26
|
+
# various searchable notes
|
|
27
|
+
%i[
|
|
28
|
+
accessrestrict
|
|
29
|
+
accruals
|
|
30
|
+
acqinfo
|
|
31
|
+
altformavail
|
|
32
|
+
appraisal
|
|
33
|
+
arrangement
|
|
34
|
+
bibliography
|
|
35
|
+
bioghist
|
|
36
|
+
custodhist
|
|
37
|
+
fileplan
|
|
38
|
+
note
|
|
39
|
+
odd
|
|
40
|
+
originalsloc
|
|
41
|
+
otherfindaid
|
|
42
|
+
phystech
|
|
43
|
+
prefercite
|
|
44
|
+
processinfo
|
|
45
|
+
relatedmaterial
|
|
46
|
+
scopecontent
|
|
47
|
+
separatedmaterial
|
|
48
|
+
userestrict
|
|
49
|
+
].each do |k|
|
|
50
|
+
# many of the notes support various markup so we want everything but the heading
|
|
51
|
+
t.send(k, path: "#{prefix}#{k}/*[local-name()!=\"head\"]", index_as: %i[displayable searchable])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# various searchable notes in the did
|
|
55
|
+
%i[
|
|
56
|
+
abstract
|
|
57
|
+
materialspec
|
|
58
|
+
physloc
|
|
59
|
+
].each do |k|
|
|
60
|
+
t.send(k, path: "#{prefix}did/#{k}", index_as: %i[displayable searchable])
|
|
61
|
+
end
|
|
62
|
+
t.did_note(path: "#{prefix}did/note", index_as: %i[displayable searchable]) # conflicts with top-level note
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arclight
|
|
4
|
+
##
|
|
5
|
+
# An module to extend SolrEad::Indexer behaviors to allow us to add
|
|
6
|
+
# or override behaviors that require knowledge of the entire XML document.
|
|
7
|
+
module SolrEadIndexerExt
|
|
8
|
+
def additional_component_fields(node, addl_fields = {})
|
|
9
|
+
solr_doc = super
|
|
10
|
+
|
|
11
|
+
add_count_of_child_compontents(node, solr_doc)
|
|
12
|
+
add_ancestral_titles(node, solr_doc)
|
|
13
|
+
add_ancestral_ids(node, solr_doc)
|
|
14
|
+
|
|
15
|
+
add_collection_creator_to_component(node, solr_doc)
|
|
16
|
+
|
|
17
|
+
add_self_or_parents_restrictions(node, solr_doc)
|
|
18
|
+
|
|
19
|
+
add_self_or_parents_terms(node, solr_doc)
|
|
20
|
+
|
|
21
|
+
solr_doc
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delete_all
|
|
25
|
+
solr.delete_by_query('*:*')
|
|
26
|
+
solr.commit
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Note that we need to redo what solr_ead does for ids due to our normalization process
|
|
32
|
+
def add_ancestral_ids(node, solr_doc)
|
|
33
|
+
@parent_id_name ||= Solrizer.solr_name('parent', :stored_sortable)
|
|
34
|
+
@parent_ids_field_name ||= Solrizer.solr_name('parent', :displayable)
|
|
35
|
+
@parent_ids_search_field_name ||= Solrizer.solr_name('parent', :searchable)
|
|
36
|
+
|
|
37
|
+
ids = ancestral_ids(node)
|
|
38
|
+
solr_doc[@parent_ids_field_name] = ids
|
|
39
|
+
solr_doc[@parent_ids_search_field_name] = ids
|
|
40
|
+
solr_doc[@parent_id_name] = ids.last
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Note that we need to redo what solr_ead does for titles due to our normalization process
|
|
44
|
+
def add_ancestral_titles(node, solr_doc)
|
|
45
|
+
@parent_titles_field_name ||= Solrizer.solr_name('parent_unittitles', :displayable)
|
|
46
|
+
@parent_titles_search_field_name ||= Solrizer.solr_name('parent_unittitles', :searchable)
|
|
47
|
+
@collection_facet_name ||= Solrizer.solr_name('collection', :facetable)
|
|
48
|
+
@collection_name ||= Solrizer.solr_name('collection', :displayable)
|
|
49
|
+
|
|
50
|
+
titles = ancestral_titles(node)
|
|
51
|
+
solr_doc[@parent_titles_field_name] = titles
|
|
52
|
+
solr_doc[@parent_titles_search_field_name] = titles
|
|
53
|
+
solr_doc[@collection_name] = [titles.first] # collection is always on top
|
|
54
|
+
solr_doc[@collection_facet_name] = [titles.first]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def add_count_of_child_compontents(node, solr_doc)
|
|
58
|
+
@child_component_count_name ||= Solrizer.solr_name('child_component_count', type: :integer)
|
|
59
|
+
|
|
60
|
+
solr_doc[@child_component_count_name] = node.xpath('count(c)').to_i
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def ancestral_ids(node)
|
|
64
|
+
ancestral_visit(node, :normalized_component_id, :normalized_collection_id)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def ancestral_titles(node)
|
|
68
|
+
ancestral_visit(node, :normalized_component_title, :normalized_collection_title)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# visit each component's parent and finish with a visit on the collection
|
|
72
|
+
def ancestral_visit(node, component_fn, collection_fn, results = [])
|
|
73
|
+
while node.parent && node.parent.name == 'c'
|
|
74
|
+
parent = node.parent
|
|
75
|
+
results << send(component_fn, parent)
|
|
76
|
+
node = parent
|
|
77
|
+
end
|
|
78
|
+
results << send(collection_fn, node)
|
|
79
|
+
results.reverse
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def normalized_component_title(node)
|
|
83
|
+
data = extract_title_and_dates(node)
|
|
84
|
+
normalize_title(data)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def normalized_collection_title(node)
|
|
88
|
+
data = extract_title_and_dates(node, '//archdesc/')
|
|
89
|
+
normalize_title(data)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def normalize_title(data)
|
|
93
|
+
Arclight::NormalizedTitle.new(
|
|
94
|
+
data[:title],
|
|
95
|
+
Arclight::NormalizedDate.new(
|
|
96
|
+
data[:unitdate_inclusive],
|
|
97
|
+
data[:unitdate_bulk],
|
|
98
|
+
data[:unitdate_other]
|
|
99
|
+
).to_s
|
|
100
|
+
).to_s
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# TODO: these xpaths should be DRY'd up -- they're in both terminologies
|
|
104
|
+
def extract_title_and_dates(node, prefix = nil)
|
|
105
|
+
data = {
|
|
106
|
+
title: node.at_xpath("#{prefix}did/unittitle"),
|
|
107
|
+
unitdate_inclusive: node.at_xpath("#{prefix}did/unitdate[@type=\"inclusive\"]"),
|
|
108
|
+
unitdate_bulk: node.at_xpath("#{prefix}did/unitdate[@type=\"bulk\"]"),
|
|
109
|
+
unitdate_other: node.at_xpath("#{prefix}did/unitdate[not(@type)]")
|
|
110
|
+
}
|
|
111
|
+
data.each do |k, v|
|
|
112
|
+
data[k] = v.text if v
|
|
113
|
+
end
|
|
114
|
+
data
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def normalized_component_id(node)
|
|
118
|
+
Arclight::NormalizedId.new(node['id'].to_s).to_s
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def normalized_collection_id(node)
|
|
122
|
+
Arclight::NormalizedId.new(node.document.at_xpath('//eadid').text).to_s
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# This mimics similar behavior in Arclight::CustomDocument
|
|
126
|
+
def add_collection_creator_to_component(node, solr_doc)
|
|
127
|
+
field_name = Solrizer.solr_name('collection_creator', :displayable)
|
|
128
|
+
repository = solr_doc[Solrizer.solr_name('repository', :displayable)]
|
|
129
|
+
creators = node.xpath('//archdesc/did/origination[@label="creator"]/*/text()').map(&:text)
|
|
130
|
+
solr_doc[field_name] = creators - [repository]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def parent_check_list(node, root_path, element_path, results = [])
|
|
134
|
+
orginal_node = node
|
|
135
|
+
results = node.xpath("#{root_path}/#{element_path}").map(&:text)
|
|
136
|
+
# if current restriction return, else go up to parent and check
|
|
137
|
+
while node.parent.name == 'c' && results.blank?
|
|
138
|
+
parent = node.parent
|
|
139
|
+
results = parent.xpath("#{root_path}/#{element_path}").map(&:text)
|
|
140
|
+
node = parent
|
|
141
|
+
end
|
|
142
|
+
# If no parental results, check the collection
|
|
143
|
+
results = orginal_node.xpath("//archdesc/#{element_path}").map(&:text) if results.blank?
|
|
144
|
+
results.flatten # can't use with flatten! because that returns nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def add_self_or_parents_restrictions(node, solr_doc)
|
|
148
|
+
field_name = Solrizer.solr_name('parent_access_restrict', :displayable)
|
|
149
|
+
solr_doc[field_name] = parent_check_list(node, './', 'accessrestrict/p/text()')
|
|
150
|
+
solr_doc[field_name]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def add_self_or_parents_terms(node, solr_doc)
|
|
154
|
+
field_name = Solrizer.solr_name('parent_access_terms', :displayable)
|
|
155
|
+
solr_doc[field_name] = parent_check_list(node, './', 'userestrict/p/text()')
|
|
156
|
+
solr_doc[field_name]
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|