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

pulibrary / orangelight / 00dbc10b-d747-4ad7-b211-7b26d753abbb

14 Aug 2025 01:25PM UTC coverage: 0.483% (-94.9%) from 95.343%
00dbc10b-d747-4ad7-b211-7b26d753abbb

push

circleci

web-flow
Merge pull request #5181 from pulibrary/dependabot/bundler/activestorage-7.2.2.2

Bump activestorage from 7.2.2.1 to 7.2.2.2

47 of 9721 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
2
require 'faraday'
×
3

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

23
    include Requests::Bibdata
×
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
29
    def initialize(system_id:, mfhd:, patron: nil)
×
30
      @system_id = system_id
×
31
      @doc = SolrDocument.new(solr_doc(system_id))
×
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
35
      @mfhd = mfhd || @holdings.keys.first
×
36
      @patron = patron
×
37
      @location_code = @holdings[@mfhd]["location_code"] if @holdings[@mfhd].present?
×
38
      @location = load_bibdata_location
×
39
      @items = load_items
×
40
      @pick_ups = build_pick_ups
×
41
      @requestable_unrouted = build_requestable
×
42
      @requestable = route_requests(@requestable_unrouted)
×
43
      @ctx_obj = Requests::SolrOpenUrlContext.new(solr_doc: doc)
×
44
    end
×
45

46
    delegate :user, to: :patron
×
47

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

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

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

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

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

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

81
    def recap?
×
82
      return false if location.blank?
×
83
      location[:remote_storage] == "recap_rmt"
×
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
89
    def load_items
×
90
      return nil if too_many_items?
×
91
      mfhd_items = load_items_by_mfhd
×
92
      mfhd_items.empty? ? nil : mfhd_items.with_indifferent_access
×
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
98
    def hidden_field_metadata
×
99
      {
×
100
        title: doc["title_citation_display"],
×
101
        author: doc["author_citation_display"],
×
102
        isbn: doc["isbn_s"]&.values_at(0),
×
103
        date: doc["pub_date_display"]
×
104
      }
×
105
    end
×
106

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

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

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

117
    def scsb_location
×
118
      doc['location_code_s'].first
×
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
124
    def too_many_items?
×
125
      holding = holdings[@mfhd]
×
126
      items = holding.try(:[], "items")
×
127
      return false if items.blank?
×
128

129
      return true if items.count > 500
×
130

131
      false
×
132
    end
×
133

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
139
      def build_requestable
×
140
        return [] if doc._source.blank?
×
141
        if doc.scsb_record?
×
142
          build_scsb_requestable
×
143
        elsif items.present?
×
144
          # for single aeon item, ends up in this statement
145
          build_requestable_with_items
×
146
        else
×
147
          # for too many aeon items, ends up in this statement
148
          build_requestable_from_data
×
149
        end
×
150
      end
×
151

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

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

165
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
166
      def build_holding_scsb_items(id:, values:, availability_data:, requestable_items:)
×
167
        values_items = values['items']
×
168
        return requestable_items if values_items.blank?
×
169
        barcodesort = build_barcode_sort(items: values_items, availability_data:)
×
170
        barcodesort.each_value do |item|
×
171
          item['location_code'] = location_code
×
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]),
×
173
                                            location:)
×
174
          requestable_items << Requests::Requestable.new(**params)
×
175
        end
×
176
        requestable_items
×
177
      end
×
178

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

194
      # :reek:DuplicateMethodCall
195
      def status_label(item:, availability_data:)
×
196
        item_object = Item.new item
×
197
        if item_object.not_a_work_order? && availability_data.empty?
×
198
          "Unavailable"
×
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."
×
200
          "In Process"
×
201
        else
×
202
          item_object.status_label
×
203
        end
×
204
      end
×
205

206
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
207
      def build_requestable_with_items
×
208
        requestable_items = []
×
209
        barcodesort = {}
×
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
212
        items.each do |holding_id, mfhd_items|
×
213
          next if mfhd != holding_id
×
214
          requestable_items = build_requestable_from_mfhd_items(requestable_items:, holding_id:, mfhd_items:, barcodesort:)
×
215
        end
×
216
        requestable_items.compact
×
217
      end
×
218

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

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

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

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

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

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

268
      # This method will always return a Requestable object where .item is some kind of placeholder item, like NullItem
269
      def build_requestable_from_holding(holding_id, holding)
×
270
        return if holding.blank?
×
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({}))
×
272
        Requests::Requestable.new(**params)
×
273
      end
×
274

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

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

293
      def build_requestable_location(params)
×
294
        location = params[:location]
×
295
        location_object = Location.new location
×
296
        location["delivery_locations"] = location_object.build_delivery_locations if location_object.delivery_locations.present?
×
297
        location
×
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
304
      def load_items_by_mfhd
×
305
        mfhd_items = {}
×
306
        mfhd_items[@mfhd] = items_by_mfhd(@system_id, @mfhd)
×
307
        mfhd_items
×
308
      end
×
309

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

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

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

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