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

pulibrary / bibdata / 373ad6ff-fad2-405a-ab26-3d30fb5ceecf

24 Dec 2024 08:24PM UTC coverage: 91.938% (+0.08%) from 91.859%
373ad6ff-fad2-405a-ab26-3d30fb5ceecf

Pull #2563

circleci

maxkadel
Put attaching xml files in their own batch
Pull Request #2563: I2321 Shift SCSB full index tasks into separate background jobs

152 of 156 new or added lines in 10 files covered. (97.44%)

65 existing lines in 17 files now uncovered.

3478 of 3783 relevant lines covered (91.94%)

366.14 hits per line

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

72.73
/app/controllers/bibliographic_controller.rb
1
class BibliographicController < ApplicationController # rubocop:disable Metrics/ClassLength
1✔
2
  include FormattingConcern
1✔
3

4
  def adapter
1✔
5
    @adapter ||= AlmaAdapter.new
49✔
6
  end
7

8
  def index
1✔
9
    if params[:bib_id]
4✔
10
      if params.fetch(:holdings_only, '0') == '1'
3✔
11
        redirect_to action: :bib_holdings, bib_id: sanitized_bibid, status: :moved_permanently
1✔
12
      elsif params.fetch(:items_only, '0') == '1'
2✔
13
        redirect_to action: :bib_items, bib_id: sanitized_bibid, status: :moved_permanently
1✔
14
      else
15
        redirect_to action: :bib, bib_id: sanitized_bibid, status: :moved_permanently
1✔
16
      end
17
    else
18
      render plain: "Record please supply a bib id", status: :not_found
1✔
19
    end
20
  end
21

22
  # Returns availability for a single ID
23
  # Client: This endpoint is used by orangelight to render status on the catalog
24
  #   show page
25
  def availability
1✔
26
    id = params[:bib_id]
2✔
27
    availability = adapter.get_availability_one(id:, deep_check: (params[:deep] == "true"))
2✔
28
    respond_to do |wants|
1✔
29
      wants.json { render json: availability }
2✔
30
    end
31
  rescue => e
32
    handle_alma_exception(exception: e, message: "Failed to retrieve availability for ID: #{id}")
1✔
33
  end
34

35
  # Returns availability for multiple IDs
36
  # Client: This endpoint is used by orangelight to render status on the catalog
37
  #   search results page
38
  def availability_many
1✔
39
    ids = (params[:bib_ids] || "").split(",")
4✔
40
    availability = adapter.get_availability_many(ids:, deep_check: ActiveModel::Type::Boolean.new.cast(params[:deep]))
4✔
41
    respond_to do |wants|
2✔
42
      wants.json { render json: availability }
4✔
43
    end
44
  rescue => e
45
    handle_alma_exception(exception: e, message: "Failed to retrieve availability for IDs: #{ids}")
2✔
46
  end
47

48
  # Returns availability for a single holding in a bib record
49
  # Client: This endpoint is used by Requests to populate a request form and
50
  #   submit requests to the ILS
51
  def availability_holding
1✔
52
    if params[:bib_id] && params[:holding_id]
6✔
53
      availability = adapter.get_availability_holding(id: params[:bib_id], holding_id: params[:holding_id])
6✔
54
      respond_to do |wants|
5✔
55
        wants.json { render json: availability, status: availability.nil? ? 404 : 200 }
10✔
56
      end
57
    else
58
      render plain: "Please supply a bib id and a holding id", status: :not_found
×
59
    end
60
  rescue => e
61
    handle_alma_exception(exception: e, message: "Failed to retrieve holdings for: #{params[:bib_id]}/#{params[:holding_id]}")
1✔
62
  end
63

64
  # Client: This endpoint is used by orangelight to present the staff view
65
  #   and sometimes by individuals to pull records from the ILS
66
  def bib
1✔
67
    opts = {
68
      holdings: params.fetch('holdings', 'true') == 'true',
13✔
69
      holdings_in_bib: params.fetch('holdings_in_bib', 'true') == 'true'
70
    }
71

72
    begin
73
      records = adapter.get_bib_record(sanitized_bibid)
13✔
74
      records.strip_non_numeric! unless opts[:holdings]
2✔
75
    rescue => e
76
      return handle_alma_exception(exception: e, message: "Failed to retrieve the record using the bib. ID: #{sanitized_bibid}")
2✔
77
    end
78

79
    if records.nil?
2✔
80
      render plain: "Record #{params[:bib_id]} not found or suppressed", status: :not_found
×
81
      Rails.logger.error "Record #{params[:bib_id]} not found or suppressed"
×
82
    else
83
      respond_to do |wants|
2✔
84
        wants.json  do
2✔
85
          json = MultiJson.dump(pass_records_through_xml_parser(records))
×
86
          render json:
×
87
        end
88
        wants.xml do
2✔
89
          xml = records_to_xml_string(records)
2✔
90
          render xml:
2✔
91
        end
92
      end
93
    end
94
  end
95

96
  # Client: Used by firestone_locator to pull bibliographic data
97
  #   Also used to pull orangelight and pul_solr test fixtures
98
  def bib_solr
1✔
99
    opts = {
100
      holdings: params.fetch('holdings', 'true') == 'true',
4✔
101
      holdings_in_bib: params.fetch('holdings_in_bib', 'true') == 'true'
102
    }
103
    records = adapter.get_bib_record(sanitized_bibid)
4✔
104
    if records.nil?
×
105
      render plain: "Record #{params[:bib_id]} not found or suppressed", status: :not_found
×
106
    else
107
      solr_doc = indexer.map_record(records)
×
108
      render json: solr_doc
×
109
    end
110
  rescue => e
111
    handle_alma_exception(exception: e, message: "Failed to retrieve the holding records for the bib. ID: #{sanitized_bibid}")
×
112
  end
113

114
  # Client: No known use cases
115
  def bib_holdings
1✔
116
    records = adapter.get_holding_records(sanitized_bibid)
7✔
117
    if records.empty?
1✔
118
      render plain: "Record #{params[:bib_id]} not found or suppressed", status: :not_found
×
119
    else
120
      respond_to do |wants|
1✔
121
        wants.json  do
1✔
122
          json = MultiJson.dump(pass_records_through_xml_parser(records))
×
123
          render json:
×
124
        end
125
        wants.xml do
1✔
126
          xml = records_to_xml_string(records)
1✔
127
          render xml:
1✔
128
        end
129
      end
130
    end
131
  rescue => e
132
    handle_alma_exception(exception: e, message: "Failed to retrieve the holding records for the bib. ID: #{sanitized_bibid}")
3✔
133
  end
134

135
  # bibliographic/:bib_id/items
136
  # Client: Used by figgy to check CDL status. Used by firestone_locator for
137
  #   call number and location data
138
  def bib_items
1✔
139
    item_keys = ["id", "pid", "perm_location", "temp_location", "cdl"]
13✔
140
    holding_summary = adapter.get_items_for_bib(sanitized_bibid).holding_summary(item_key_filter: item_keys)
13✔
141

142
    respond_to do |wants|
7✔
143
      wants.json  { render json: MultiJson.dump(add_locator_call_no(holding_summary)) }
14✔
144
      wants.xml { render xml: '<todo but="You probably want JSON anyway" />' }
7✔
145
    end
146
  rescue Alma::BibItemSet::ResponseError
147
    render_not_found(params[:bib_id])
2✔
148
  rescue => e
149
    handle_alma_exception(exception: e, message: "Failed to retrieve items for bib ID: #{sanitized_bibid}")
×
150
  end
151

152
  private
1✔
153

154
    def render_not_found(id)
1✔
155
      render plain: "Record #{id} not found or suppressed", status: :not_found
2✔
156
    end
157

158
    # Construct or access the indexing service
159
    # @return [IndexingService]
160
    def index_job_queue
1✔
161
      traject_config = Rails.application.config.traject
×
162
      solr_config = Rails.application.config.solr
×
163
      @index_job_queue ||= IndexJobQueue.new(config: traject_config['config'], url: solr_config['url'])
×
164
    end
165

166
    # Ensure that the client is authenticated and the user is a catalog administrator
167
    def protect
1✔
168
      if user_signed_in?
×
169
        render plain: "You are unauthorized", status: :forbidden if !current_user.catalog_admin?
×
170
      else
171
        redirect_to user_cas_omniauth_authorize_path
×
172
      end
173
    end
174

175
    # Generate the options for retrieving bib. records from Voyager
176
    # @return [Hash]
177
    def voyager_opts
1✔
178
      {
179
        holdings: params.fetch('holdings', 'true') == 'true',
×
180
        holdings_in_bib: params.fetch('holdings_in_bib', 'true') == 'true'
181
      }
182
    end
183

184
    # Access the URL helpers for the application
185
    # @return [Array<ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper>]
186
    def url_helpers
1✔
187
      Rails.application.routes.url_helpers
×
188
    end
189

190
    # Access the global Traject Object
191
    # @return [Traject::Indexer::MarcIndexer] the Traject indexer
192
    def indexer
1✔
193
      TRAJECT_INDEXER
×
194
    end
195

196
    # Generate the URL for the application root
197
    # @return [String] the root URL
198
    def root_url
1✔
199
      url_helpers.root_url(host: request.host_with_port)
×
200
    end
201

202
    # Generates the URL for the bibliographic record
203
    # @return [String] the URL
204
    def bib_id_url
1✔
205
      url_helpers.show_bib_url(params[:bib_id], host: request.host_with_port)
×
206
    end
207

208
    # Sanitizes the bib_id HTTP parameter
209
    # @return [String]
210
    def sanitized_bibid
1✔
211
      CGI.escape(params[:bib_id])
45✔
212
    end
213

214
    def add_locator_call_no(records)
1✔
215
      records.each do |location, holdings|
7✔
216
        next unless location == "firestone$stacks"
10✔
217
        holdings.each do |holding|
3✔
218
          holding["sortable_call_number"] = sortable_call_number(holding["call_number"])
3✔
219
        end
220
      end
221
    end
222

223
    def sortable_call_number(call_no)
1✔
224
      return call_no unless /^[A-Za-z]/.match?(call_no)
3✔
225
      call_no = make_sortable_call_number(call_no)
3✔
226
      lsort_result = Lcsort.normalize(call_no)
3✔
227
      return lsort_result.gsub('..', '.') unless lsort_result.nil?
3✔
UNCOV
228
      force_number_part_to_have_4_digits(call_no)
×
229
    rescue
UNCOV
230
      call_no
×
231
    end
232

233
    def make_sortable_call_number(call_no)
1✔
234
      tokens = call_no.split(" ")
3✔
235
      needs_adjustment = ["oversize", "folio"].include? tokens.first.downcase
3✔
236
      return call_no unless needs_adjustment
3✔
237
      # Move the first token (e.g. Oversize or Folio) to the end
238
      (tokens[1..] << tokens[0]).join(" ")
1✔
239
    end
240

241
    # This routine adjust something from "A53.blah" to "A0053.blah" for sorting purposes
242
    #
243
    def force_number_part_to_have_4_digits(call_no)
1✔
UNCOV
244
      dot_parts = call_no.tr(',', '.').split('.')
×
UNCOV
245
      return call_no if dot_parts.count <= 1
×
246

UNCOV
247
      parts = dot_parts[0].scan(/[A-Za-z]+|\d+/)
×
248
      parts[1] = parts[1].rjust(4, '0')
×
249
      dot_parts[0] = parts.join('.')
×
UNCOV
250
      dot_parts.join('.')
×
251
    end
252
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