• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / orangelight / 837bc772-cf8b-4e12-a64f-c08713fcbba5

08 Jul 2025 03:25PM UTC coverage: 95.353% (-0.01%) from 95.366%
837bc772-cf8b-4e12-a64f-c08713fcbba5

Pull #4962

circleci

web-flow
Display format as a badge within search results (#4965)

* Update Readme - Development - pre-requisites section

* Add components for displaying format badges

* Use Format badge in place of format_icon helper method

---------

Co-authored-by: Christina Chortaria <actspatial@gmail.com>
Pull Request #4962: Orangelight pos workcycle 07-07-2025

81 of 85 new or added lines in 14 files covered. (95.29%)

1 existing line in 1 file now uncovered.

6032 of 6326 relevant lines covered (95.35%)

1515.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.28
/app/models/solr_document.rb
1
# frozen_string_literal: true
2
require_relative 'solr_document/identifier'
3✔
3

4
class SolrDocument
3✔
5
  include Blacklight::Solr::Document
3✔
6
  include Orangelight::Document::Export
3✔
7
  include Orangelight::Document::Alma
3✔
8
  include Orangelight::Document::Scsb
3✔
9
  include Orangelight::Document::StandardNumbers
3✔
10

11
  # Explicitly required for sneakers
12
  include Blacklight::Document::Extensions
3✔
13
  include Blacklight::Document::SemanticFields
3✔
14

15
  # The following shows how to setup this blacklight document to display marc documents
16
  extension_parameters[:marc_source_field] = :id
3✔
17
  extension_parameters[:marc_format_type] = :marcxml
3✔
18
  use_extension(Blacklight::Marc::DocumentExtension) do |document|
3✔
19
    document.key?(:id)
2,497✔
20
  end
21

22
  field_semantics.merge!(
3✔
23
    title: 'title_citation_display',
24
    creator: 'author_citation_display',
25
    language: 'language_facet',
26
    format: 'format',
27
    description: 'summary_note_display',
28
    date: 'pub_date_start_sort',
29
    publisher: 'pub_created_display',
30
    subject: 'subject_facet',
31
    type: 'format',
32
    identifier: 'isbn_s'
33
  )
34

35
  # self.unique_key = 'id'
36

37
  # Email uses the semantic field mappings below to generate the body of an email.
38
  SolrDocument.use_extension(Blacklight::Document::Email)
3✔
39

40
  # DublinCore uses the semantic field mappings below to assemble an OAI-compliant Dublin Core
41
  # document Semantic mappings of solr stored fields. Fields may be multi or single valued.
42
  # See Blacklight::Solr::Document::ExtendableClassMethods#field_semantics and
43
  # Blacklight::Solr::Document#to_semantic_values.
44
  # Recommendation: Use field names from Dublin Core
45
  use_extension(Blacklight::Document::DublinCore)
3✔
46

47
  ## Adds RIS
48
  use_extension(Blacklight::Document::Ris)
3✔
49

50
  ## Adds JSON-LD
51
  use_extension(Blacklight::Document::JsonLd)
3✔
52

53
  ## Adds the methods needed for CiteProc citations,
54
  # Including MLA, APA, and Chicago
55
  use_extension(Blacklight::Document::CiteProc)
3✔
56

57
  ## Adds MLA html
58
  use_extension(Blacklight::Document::Mla)
3✔
59

60
  # Adds APA html
61
  use_extension(Blacklight::Document::Apa)
3✔
62

63
  # Adds Chicago Author Date html
64
  use_extension(Blacklight::Document::ChicagoAuthorDate)
3✔
65

66
  # Adds Chicago Note Bibliography html
67
  use_extension(Blacklight::Document::ChicagoNotesBibliography)
3✔
68

69
  def identifier_data
3✔
70
    values = identifiers.each_with_object({}) do |identifier, hsh|
928✔
71
      hsh[identifier.data_key.to_sym] ||= []
1,457✔
72
      hsh[identifier.data_key.to_sym] << identifier.value
1,457✔
73
    end
74

75
    values[:'bib-id'] = id unless id.nil?
928✔
76
    values
928✔
77
  end
78

79
  def identifiers
3✔
80
    @identifiers ||= identifier_keys.flat_map do |key|
1,860✔
81
      fetch(key, []).map do |value|
1,866✔
82
        SolrDocument::Identifier.new(key, value)
1,466✔
83
      end
84
    end.compact
85
  end
86

87
  # Retrieve the value of the ARK identifier
88
  # @return [String] the ARK for the resource
89
  def ark
3✔
90
    return unless full_ark
4✔
91
    m = /.*(ark:(.*))/.match(full_ark)
2✔
92
    m[1]
2✔
93
  end
94

95
  # Retrieve the electronic access information
96
  # @return [String] electronic access value
97
  def doc_electronic_access
3✔
98
    string_values = first('electronic_access_1display') || '{}'
115✔
99
    JSON.parse(string_values).delete_if { |k, _v| k == 'iiif_manifest_paths' }
184✔
100
  end
101

102
  # Retrieve electronic portfolio values and parse
103
  # @return [Array<Hash>] array of electronic portfolio hashes
104
  def electronic_portfolios
3✔
105
    values = fetch('electronic_portfolio_s', [])
1,059✔
106
    values.map { |v| JSON.parse(v) }
1,587✔
107
  end
108

109
  # Parse IIIF Manifest links from the electronic access information
110
  # @return [Hash] IIIF Manifests information
111
  def iiif_manifests
3✔
112
    string_values = first('electronic_access_1display') || '{}'
5✔
113
    values = JSON.parse(string_values)
5✔
114
    values.fetch('iiif_manifest_paths', {})
5✔
115
  end
116

117
  # IIIF Manifest URIs from the electronic access information
118
  # @return [Array<String>] URIs to IIIF Manifests
119
  def iiif_manifest_uris
3✔
120
    iiif_manifests.values
×
121
  end
122

123
  # The default IIIF Manifest URI from the electronic access information
124
  # @return [String] URIs to IIIF Manifests
125
  def iiif_manifest_uri
3✔
126
    iiif_manifest_uris.first
×
127
  end
128

129
  # Returns the MMS_IDs found in the electronic_access_1display display for URLs that follow the
130
  # pattern https://catalog.princeton.edu/catalog\{mms_id}#view except the one for the current ID.
131
  # These URLs are found when the Figgy manifest is registered for another (related) MMS_ID rather
132
  # than for the current one.
133
  def related_bibs_iiif_manifest
3✔
134
    @related_bibs_iiif_manifest ||= begin
114✔
135
      string_values = first('electronic_access_1display') || '{}'
114✔
136
      values = JSON.parse(string_values)
114✔
137
      mms_ids = values.keys.map { |key| key[/https\:\/\/catalog.princeton.edu\/catalog\/(\d*)#view/, 1] }.compact.uniq
176✔
138
      mms_ids.map { |id| ensure_voyager_to_alma_id(id) }.select { |mms_id| mms_id != id }
142✔
139
    end
140
  rescue => ex
141
    Rails.logger.error "Error calculating related_bibs_iiif_manifest for #{id}: #{ex.message}"
×
142
    []
×
143
  end
144

145
  # Makes sure an ID is an Alma ID or converts it to one if it is not.
146
  def ensure_voyager_to_alma_id(id)
3✔
147
    return id if id.length > 7 && id.start_with?("99")
14✔
148
    "99#{id}3506421"
8✔
149
  end
150

151
  # Retrieve the set of documents linked to this Object using a Solr Field
152
  # @param field [String] the field for this Object which contains the foreign document keys
153
  # @param query_field [String] the field in the linked documents to use as a key
154
  # @return [LinkedDocumentResolver::LinkedDocuments]
155
  def linked_records(field:, query_field: 'id', maximum_records: Orangelight.config['show_page']['linked_documents']['maximum'])
3✔
156
    sibling_ids = clean_ids(Array.wrap(fetch(field, [])))
125✔
157
    root_id = fetch(:id)
125✔
158
    LinkedDocumentResolver::LinkedDocuments.new(siblings: sibling_ids,
125✔
159
                                                root: root_id,
160
                                                solr_field: query_field,
161
                                                maximum_records:)
162
  end
163

164
  def full_arks
3✔
165
    electronic_access_uris.select { |x| x.include?('ark:') }
13✔
166
  end
167

168
  # Retrieves electronic portfolio values from sibling documents
169
  # @return [Array<Hash>] array of electronic portfolio hashes
170
  def sibling_electronic_portfolios
3✔
171
    sibling_documents.flat_map(&:electronic_portfolios)
108✔
172
  end
173

174
  def solr_document_id
3✔
175
    self["id"]
3,151✔
176
  end
177

178
  def host_id
3✔
179
    self["contained_in_s"].reject(&:empty?) if self["contained_in_s"].present?
2,219✔
180
  end
181

182
  def bound_with?
3✔
183
    return true if host_id.present?
748✔
184
    false
693✔
185
  end
186

187
  def numismatics_record?
3✔
188
    solr_document_id&.start_with? 'coin'
1,654✔
189
  end
190

191
  def in_a_special_collection?
3✔
192
    holdings_1display = self['holdings_1display']
931✔
193
    return true unless holdings_1display
931✔
194

195
    JSON.parse(holdings_1display)
759✔
196
        &.values
197
        &.any? do |holding|
198
          location_code = holding['location_code']
971✔
199
          Bibdata.holding_locations.dig(location_code, 'aeon_location') if location_code
971✔
200
        end
201
  end
202

203
  # host_id an Array of host id(s)
204
  # appends the host_id in each host_holding
205
  # merges host_holding in holdings
206
  def holdings_with_host_id(holdings)
3✔
207
    host_id.each do |id|
74✔
208
      host_solr_document = doc_by_id(id)
110✔
209
      host_holdings = host_solr_document&.dig("holdings_1display")
110✔
210
      host_holdings_parse = JSON.parse(host_holdings || '{}')
110✔
211
      next if host_holdings_parse.blank? # do not merge an empty holding
110✔
212
      host_holding_id = host_holdings_parse.first[0]
96✔
213
      # append the host_id as mms_id in the host_holdings
214
      host_holdings_parse[host_holding_id]["mms_id"] = id
96✔
215

216
      holdings.merge!(host_holdings_parse)
96✔
217
    end
218
    holdings
74✔
219
  end
220

221
  # Returns the holdings_1display of the record plus the holdings_1display of the host record
222
  def holdings_all_display
3✔
223
    holdings = JSON.parse(self["holdings_1display"] || '{}')
1,288✔
224

225
    holdings.each do |k, _val|
1,288✔
226
      # append the solr document id in each holding
227
      holdings[k].merge!("mms_id" => solr_document_id) if holdings[k].present?
1,507✔
228
    end
229
    return holdings if host_id.blank?
1,288✔
230
    # Append the host_id in the host_holdings
231
    # merge the host_holdings in holdings
232
    holdings_with_host_id(holdings)
74✔
233
  end
234

235
  private
3✔
236

237
    def electronic_access_uris
3✔
238
      electronic_access = first('electronic_access_1display')
6✔
239
      values = JSON.parse(electronic_access)
6✔
240
      uris = values.keys
5✔
241
      if values['iiif_manifest_paths']
5✔
NEW
242
        uris.delete('iiif_manifest_paths')
×
243
        uris += values['iiif_manifest_paths'].keys
×
244
      end
245
      uris
5✔
246
    rescue
247
      []
1✔
248
    end
249

250
    def full_ark
3✔
251
      full_arks.first
6✔
252
    end
253

254
    def clean_ids(id_values)
3✔
255
      out = id_values.map { |id| id.delete('#') }
672✔
256
      # Strip all non-ascii characters from ids
257
      out.map { |id| id.gsub(/[^[:ascii:]]/, "") }
672✔
258
    end
259

260
    def identifier_keys
3✔
261
      %w[
933✔
262
        isbn_s
263
        oclc_s
264
      ]
265
    end
266

267
    # Retrieves sibling documents linked by values on the other_version_s field
268
    # @return [Array<SolrDocument>] array of sibling solr documents
269
    def sibling_documents
3✔
270
      sibling_ids = clean_ids(Array.wrap(fetch('other_version_s', [])))
108✔
271
      root_id = fetch(:id)
108✔
272
      linked_documents = LinkedDocumentResolver::LinkedDocuments.new(siblings: sibling_ids,
108✔
273
                                                                     root: root_id,
274
                                                                     solr_field: 'other_version_s')
275
      linked_documents.siblings
108✔
276
    end
277

278
    def doc_by_id(id)
3✔
279
      params = { q: "id:#{RSolr.solr_escape(id)}" }
107✔
280
      solr_response = Blacklight.default_index.connection.get('select', params:)
107✔
281
      solr_response["response"]["docs"].first
107✔
282
    end
283
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc