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

pulibrary / orangelight / 0e37073d-109c-440c-949f-49d2aa86311b

18 Aug 2025 09:05PM UTC coverage: 0.482% (-94.9%) from 95.343%
0e37073d-109c-440c-949f-49d2aa86311b

push

circleci

web-flow
Replace per_page_options_for_select with custom component to avoid deprecation issue (#5186)

* Start creating new component to address deprecaton warning

* Replace per_page_options_for_select with custom component to avoid deprecation issue

Co-authored-by: Jane Sandberg <sandbergja@users.noreply.github.com>

---------

Co-authored-by: Ryan Jensen <rj1044@princeton.edu>
Co-authored-by: Jane Sandberg <sandbergja@users.noreply.github.com>

0 of 33 new or added lines in 1 file covered. (0.0%)

9374 existing lines in 213 files now uncovered.

47 of 9753 relevant lines covered (0.48%)

0.01 hits per line

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

0.0
/app/models/requests/form.rb
1
# frozen_string_literal: true
UNCOV
2
require 'faraday'
×
3

UNCOV
4
module Requests
×
5
  # Request class is responsible of building a request
6
  # using items and location of the holding
UNCOV
7
  class Form
×
UNCOV
8
    attr_reader :system_id
×
UNCOV
9
    attr_reader :mfhd
×
UNCOV
10
    attr_reader :patron
×
UNCOV
11
    attr_reader :doc
×
UNCOV
12
    attr_reader :requestable
×
UNCOV
13
    attr_reader :requestable_unrouted
×
UNCOV
14
    attr_reader :holdings
×
UNCOV
15
    attr_reader :location
×
UNCOV
16
    attr_reader :location_code
×
UNCOV
17
    attr_reader :items
×
UNCOV
18
    attr_reader :pick_ups
×
UNCOV
19
    alias default_pick_ups pick_ups
×
UNCOV
20
    delegate :ctx, to: :@ctx_obj
×
UNCOV
21
    delegate :eligible_for_library_services?, to: :patron
×
22

UNCOV
23
    include Requests::Bibdata
×
UNCOV
24
    include Requests::Scsb
×
25

26
    # @option opts [String] :system_id A bib record id or a special collection ID value
27
    # @option opts [Fixnum] :mfhd alma holding id
28
    # @option opts [Patron] :patron current Patron object
UNCOV
29
    def initialize(system_id:, mfhd:, patron: nil)
×
UNCOV
30
      @system_id = system_id
×
UNCOV
31
      @doc = SolrDocument.new(solr_doc(system_id))
×
UNCOV
32
      @holdings = JSON.parse(doc[:holdings_1display] || '{}')
×
33
      # scsb items are the only ones that come in without a MFHD parameter from the catalog now
34
      # set it for them, because they only ever have one location
UNCOV
35
      @mfhd = mfhd || @holdings.keys.first
×
UNCOV
36
      @patron = patron
×
UNCOV
37
      @location_code = @holdings[@mfhd]["location_code"] if @holdings[@mfhd].present?
×
UNCOV
38
      @location = load_bibdata_location
×
UNCOV
39
      @items = load_items
×
UNCOV
40
      @pick_ups = build_pick_ups
×
UNCOV
41
      @requestable_unrouted = build_requestable
×
UNCOV
42
      @requestable = route_requests(@requestable_unrouted)
×
UNCOV
43
      @ctx_obj = Requests::SolrOpenUrlContext.new(solr_doc: doc)
×
UNCOV
44
    end
×
45

UNCOV
46
    delegate :user, to: :patron
×
47

UNCOV
48
    def requestable?
×
UNCOV
49
      requestable.size.positive?
×
UNCOV
50
    end
×
51

UNCOV
52
    def first_filtered_requestable
×
UNCOV
53
      requestable&.first
×
UNCOV
54
    end
×
55

56
    # Does this request object have any available copies?
UNCOV
57
    def any_loanable_copies?
×
UNCOV
58
      requestable_unrouted.any? do |requestable|
×
UNCOV
59
        !(requestable.charged? || (requestable.aeon? || !requestable.circulates? || requestable.partner_holding? || requestable.on_reserve?))
×
UNCOV
60
      end
×
UNCOV
61
    end
×
62

UNCOV
63
    def any_enumerated?
×
UNCOV
64
      requestable_unrouted.any?(&:enumerated?)
×
UNCOV
65
    end
×
66

UNCOV
67
    def route_requests(requestable_items)
×
UNCOV
68
      routed_requests = []
×
UNCOV
69
      return [] if requestable_items.blank?
×
UNCOV
70
      requestable_items.each do |requestable|
×
UNCOV
71
        router = Requests::Router.new(requestable:, any_loanable: any_loanable_copies?, patron:)
×
UNCOV
72
        routed_requests << router.routed_request
×
UNCOV
73
      end
×
UNCOV
74
      routed_requests
×
UNCOV
75
    end
×
76

UNCOV
77
    def serial?
×
UNCOV
78
      doc[:format].present? && doc[:format].include?('Journal')
×
UNCOV
79
    end
×
80

UNCOV
81
    def recap?
×
UNCOV
82
      return false if location.blank?
×
UNCOV
83
      location[:remote_storage] == "recap_rmt"
×
UNCOV
84
    end
×
85

86
    # returns nil if there are no attached items
87
    # if mfhd set returns only items associated with that mfhd
88
    # if no mfhd returns items sorted by mfhd
UNCOV
89
    def load_items
×
UNCOV
90
      return nil if too_many_items?
×
UNCOV
91
      mfhd_items = load_items_by_mfhd
×
UNCOV
92
      mfhd_items.empty? ? nil : mfhd_items.with_indifferent_access
×
UNCOV
93
    end
×
94

95
    # returns basic metadata for hidden fields on the request form via solr_doc values
96
    # Fields to return all keys are arrays
97
    ## Add more fields here as needed
UNCOV
98
    def hidden_field_metadata
×
UNCOV
99
      {
×
UNCOV
100
        title: doc["title_citation_display"],
×
UNCOV
101
        author: doc["author_citation_display"],
×
UNCOV
102
        isbn: doc["isbn_s"]&.values_at(0),
×
UNCOV
103
        date: doc["pub_date_display"]
×
UNCOV
104
      }
×
UNCOV
105
    end
×
106

107
    # Calls Requests::BibdataService to get the delivery_locations
108

UNCOV
109
    def ill_eligible?
×
110
      requestable.any? { |r| r.services.include? 'ill' }
×
UNCOV
111
    end
×
112

UNCOV
113
    def other_id
×
UNCOV
114
      doc['other_id_s'].first
×
UNCOV
115
    end
×
116

UNCOV
117
    def scsb_location
×
UNCOV
118
      doc['location_code_s'].first
×
UNCOV
119
    end
×
120

121
    # holdings: The holdings1_display from the SolrDocument
122
    # holding: The holding of the holding_id(mfhd) from the SolrDocument
123
    # happens on 'click' the 'Request' button
UNCOV
124
    def too_many_items?
×
UNCOV
125
      holding = holdings[@mfhd]
×
UNCOV
126
      items = holding.try(:[], "items")
×
UNCOV
127
      return false if items.blank?
×
128

UNCOV
129
      return true if items.count > 500
×
130

UNCOV
131
      false
×
UNCOV
132
    end
×
133

UNCOV
134
    private
×
135

136
      ### builds a list of possible requestable items
137
      # returns a collection of requestable objects or nil
138
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
UNCOV
139
      def build_requestable
×
UNCOV
140
        return [] if doc._source.blank?
×
UNCOV
141
        if doc.scsb_record?
×
UNCOV
142
          build_scsb_requestable
×
UNCOV
143
        elsif items.present?
×
144
          # for single aeon item, ends up in this statement
UNCOV
145
          build_requestable_with_items
×
UNCOV
146
        else
×
147
          # for too many aeon items, ends up in this statement
UNCOV
148
          build_requestable_from_data
×
UNCOV
149
        end
×
UNCOV
150
      end
×
151

UNCOV
152
      def availability_data(id)
×
UNCOV
153
        @availability_data ||= items_by_id(id, scsb_owning_institution(scsb_location))
×
UNCOV
154
      end
×
155

156
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
UNCOV
157
      def build_scsb_requestable
×
UNCOV
158
        requestable_items = []
×
UNCOV
159
        holdings.each do |id, values|
×
UNCOV
160
          requestable_items = build_holding_scsb_items(id:, values:, availability_data: availability_data(other_id), requestable_items:)
×
UNCOV
161
        end
×
UNCOV
162
        requestable_items
×
UNCOV
163
      end
×
164

165
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
UNCOV
166
      def build_holding_scsb_items(id:, values:, availability_data:, requestable_items:)
×
UNCOV
167
        values_items = values['items']
×
UNCOV
168
        return requestable_items if values_items.blank?
×
UNCOV
169
        barcodesort = build_barcode_sort(items: values_items, availability_data:)
×
UNCOV
170
        barcodesort.each_value do |item|
×
UNCOV
171
          item['location_code'] = location_code
×
UNCOV
172
          params = build_requestable_params(item: Item.new(item.with_indifferent_access), holding: Holding.new(mfhd_id: id.to_sym.to_s, holding_data: holdings[id]),
×
UNCOV
173
                                            location:)
×
UNCOV
174
          requestable_items << Requests::Requestable.new(**params)
×
UNCOV
175
        end
×
UNCOV
176
        requestable_items
×
UNCOV
177
      end
×
178

UNCOV
179
      def build_barcode_sort(items:, availability_data:)
×
UNCOV
180
        barcodesort = {}
×
UNCOV
181
        items.each do |item|
×
UNCOV
182
          item[:status_label] = status_label(item:, availability_data:)
×
UNCOV
183
          barcodesort[item['barcode']] = item
×
UNCOV
184
        end
×
UNCOV
185
        availability_data.each do |item|
×
UNCOV
186
          barcode_item = barcodesort[item['itemBarcode']]
×
UNCOV
187
          next if barcode_item.blank? || barcode_item["status_source"] == "work_order" || item['errorMessage'].present?
×
UNCOV
188
          barcode_item['status_label'] = item['itemAvailabilityStatus']
×
UNCOV
189
          barcode_item['status'] = nil
×
UNCOV
190
        end
×
UNCOV
191
        barcodesort
×
UNCOV
192
      end
×
193

194
      # :reek:DuplicateMethodCall
UNCOV
195
      def status_label(item:, availability_data:)
×
UNCOV
196
        item_object = Item.new item
×
UNCOV
197
        if item_object.not_a_work_order? && availability_data.empty?
×
UNCOV
198
          "Unavailable"
×
UNCOV
199
        elsif item_object.not_a_work_order? && item_object.status_label == 'Item in place' && availability_data.size == 1 && availability_data.first['errorMessage'] == "Bib Id doesn't exist in SCSB database."
×
UNCOV
200
          "In Process"
×
UNCOV
201
        else
×
UNCOV
202
          item_object.status_label
×
UNCOV
203
        end
×
UNCOV
204
      end
×
205

206
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
UNCOV
207
      def build_requestable_with_items
×
UNCOV
208
        requestable_items = []
×
UNCOV
209
        barcodesort = {}
×
UNCOV
210
        barcodesort = build_barcode_sort(items: items[mfhd], availability_data: availability_data(system_id)) if recap?
×
211
        # items from the availability lookup using the Bibdata Service
UNCOV
212
        items.each do |holding_id, mfhd_items|
×
UNCOV
213
          next if mfhd != holding_id
×
UNCOV
214
          requestable_items = build_requestable_from_mfhd_items(requestable_items:, holding_id:, mfhd_items:, barcodesort:)
×
UNCOV
215
        end
×
UNCOV
216
        requestable_items.compact
×
UNCOV
217
      end
×
218

219
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables or empty array
UNCOV
220
      def build_requestable_from_data
×
UNCOV
221
        return if doc[:holdings_1display].blank?
×
UNCOV
222
        return [] if holdings[@mfhd].blank?
×
223

UNCOV
224
        [build_requestable_from_holding(@mfhd, holdings[@mfhd].with_indifferent_access)]
×
UNCOV
225
      end
×
226

UNCOV
227
      def build_requestable_from_mfhd_items(requestable_items:, holding_id:, mfhd_items:, barcodesort:)
×
UNCOV
228
        if !mfhd_items.empty?
×
UNCOV
229
          mfhd_items.each do |item|
×
UNCOV
230
            requestable_items << build_requestable_mfhd_item(holding_id, item, barcodesort)
×
UNCOV
231
          end
×
UNCOV
232
        else
×
UNCOV
233
          requestable_items << build_requestable_from_holding(holding_id, holdings[holding_id])
×
UNCOV
234
        end
×
UNCOV
235
        requestable_items.compact
×
UNCOV
236
      end
×
237

UNCOV
238
      def holding_data(item, holding_id, item_location_code)
×
UNCOV
239
        if item["in_temp_library"] && item["temp_location_code"] != "RES_SHARE$IN_RS_REQ"
×
UNCOV
240
          holdings[item_location_code]
×
UNCOV
241
        else
×
UNCOV
242
          holdings[holding_id]
×
UNCOV
243
        end
×
UNCOV
244
      end
×
245

246
      # Item we get from the 'load_items' live call to bibdata
UNCOV
247
      def build_requestable_mfhd_item(holding_id, item, barcodesort)
×
UNCOV
248
        return if item['on_reserve'] == 'Y'
×
UNCOV
249
        item['status_label'] = barcodesort[item['barcode']][:status_label] unless barcodesort.empty?
×
UNCOV
250
        item_current_location = item_current_location(item)
×
UNCOV
251
        params = build_requestable_params(
×
UNCOV
252
          item: Item.new(item.with_indifferent_access),
×
UNCOV
253
          holding: Holding.new(mfhd_id: holding_id.to_sym.to_s, holding_data: holding_data(item, holding_id, item_location_code)),
×
UNCOV
254
          location: item_current_location
×
UNCOV
255
        )
×
UNCOV
256
        Requests::Requestable.new(**params)
×
UNCOV
257
      end
×
258

UNCOV
259
      def get_current_location(item_location_code:)
×
UNCOV
260
        if item_location_code != location_code
×
UNCOV
261
          @temp_locations ||= TempLocationCache.new
×
UNCOV
262
          @temp_locations.retrieve(item_location_code)
×
UNCOV
263
        else
×
UNCOV
264
          location
×
UNCOV
265
        end
×
UNCOV
266
      end
×
267

268
      # This method will always return a Requestable object where .item is some kind of placeholder item, like NullItem
UNCOV
269
      def build_requestable_from_holding(holding_id, holding)
×
UNCOV
270
        return if holding.blank?
×
UNCOV
271
        params = build_requestable_params(holding: Holding.new(mfhd_id: holding_id.to_sym.to_s, holding_data: holding), location:, item: placeholder_item_class.new({}))
×
UNCOV
272
        Requests::Requestable.new(**params)
×
UNCOV
273
      end
×
274

UNCOV
275
      def load_bibdata_location
×
UNCOV
276
        return if location_code.blank?
×
UNCOV
277
        location = get_location_data(location_code)
×
UNCOV
278
        location_object = Location.new location
×
UNCOV
279
        location[:delivery_locations] = location_object.sort_pick_ups if location_object.delivery_locations.present?
×
UNCOV
280
        location
×
UNCOV
281
      end
×
282

UNCOV
283
      def build_requestable_params(params)
×
UNCOV
284
        {
×
UNCOV
285
          bib: doc,
×
UNCOV
286
          holding: params[:holding],
×
UNCOV
287
          item: params[:item],
×
UNCOV
288
          location: build_requestable_location(params),
×
UNCOV
289
          patron:
×
UNCOV
290
        }
×
UNCOV
291
      end
×
292

UNCOV
293
      def build_requestable_location(params)
×
UNCOV
294
        location = params[:location]
×
UNCOV
295
        location_object = Location.new location
×
UNCOV
296
        location["delivery_locations"] = location_object.build_delivery_locations if location_object.delivery_locations.present?
×
UNCOV
297
        location
×
UNCOV
298
      end
×
299

300
      ## Loads item availability through the Request Bibdata service using the items_by_mfhd method
301
      # items_by_mfhd makes the availabiliy call:
302
      # bibdata_conn.get "/bibliographic/#{system_id}/holdings/#{mfhd_id}/availability.json"
303
      # rename to: load_items_by_holding_id
UNCOV
304
      def load_items_by_mfhd
×
UNCOV
305
        mfhd_items = {}
×
UNCOV
306
        mfhd_items[@mfhd] = items_by_mfhd(@system_id, @mfhd)
×
UNCOV
307
        mfhd_items
×
UNCOV
308
      end
×
309

UNCOV
310
      def items_to_symbols(items = [])
×
311
        items_with_symbols = []
×
312
        items.each do |item|
×
313
          items_with_symbols << item.with_indifferent_access
×
UNCOV
314
        end
×
315
        items_with_symbols
×
UNCOV
316
      end
×
317

UNCOV
318
      def item_current_location(item)
×
UNCOV
319
        @item_location_code = if item['in_temp_library']
×
UNCOV
320
                                item['temp_location_code']
×
UNCOV
321
                              else
×
UNCOV
322
                                item['location']
×
UNCOV
323
                              end
×
UNCOV
324
        get_current_location(item_location_code:)
×
UNCOV
325
      end
×
326

UNCOV
327
      def placeholder_item_class
×
UNCOV
328
        if too_many_items?
×
UNCOV
329
          TooManyItemsPlaceholderItem
×
UNCOV
330
        else
×
UNCOV
331
          NullItem
×
UNCOV
332
        end
×
UNCOV
333
      end
×
334

UNCOV
335
      attr_reader :item_location_code
×
UNCOV
336
  end
×
UNCOV
337
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