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

pulibrary / bibdata / 093263e6-77fd-44c7-8b76-a10bf5ed62c2

14 Feb 2025 10:16AM UTC coverage: 91.725% (-0.4%) from 92.117%
093263e6-77fd-44c7-8b76-a10bf5ed62c2

push

circleci

christinach
[#2486] Remove code related to CDL. This feature is no longer active in figgy

1 of 1 new or added line in 1 file covered. (100.0%)

14 existing lines in 2 files now uncovered.

3392 of 3698 relevant lines covered (91.73%)

375.34 hits per line

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

98.69
/app/adapters/alma_adapter/alma_item.rb
1
class AlmaAdapter
1✔
2
  class AlmaItem < SimpleDelegator
1✔
3
    attr_reader :item
1✔
4

5
    # @param item [Alma::BibItem]
6
    def initialize(item)
1✔
7
      @item = item
133✔
8
      super(item)
133✔
9
    end
10

11
    def self.reserve_location?(library_code, location_code)
1✔
12
      return false if library_code.nil? || location_code.nil?
46✔
13

14
      Rails.cache.fetch("library_#{library_code}_#{location_code}", expires_in: 30.minutes) do
46✔
15
        # We could get this information from our location table if we want to avoid the Alma API call.
16
        record = Alma::Location.find(library_code:, location_code:)
10✔
17
        record.response.dig('fulfillment_unit', 'value') == 'Reserves'
10✔
18
      end
19
    end
20

21
    def composite_location
1✔
22
      "#{library}$#{location}"
69✔
23
    end
24

25
    def composite_temp_location
1✔
26
      return unless in_temp_location?
21✔
27

28
      "#{temp_library}$#{temp_location}"
19✔
29
    end
30

31
    def composite_perm_location
1✔
32
      "#{holding_library}$#{holding_location}"
2✔
33
    end
34

35
    def composite_location_display
1✔
36
      if in_temp_location?
11✔
37
        composite_temp_location
8✔
38
      else
39
        composite_location
3✔
40
      end
41
    end
42

43
    def composite_location_label_display
1✔
44
      if in_temp_location?
11✔
45
        holding_location_label(composite_temp_location)
8✔
46
      else
47
        holding_location_label(composite_location)
3✔
48
      end
49
    end
50

51
    def composite_library_label_display
1✔
52
      if in_temp_location?
40✔
53
        holding_data.dig('temp_library', 'desc')
11✔
54
      else
55
        item_data.dig('library', 'desc')
29✔
56
      end
57
    end
58

59
    def on_reserve?
1✔
60
      AlmaItem.reserve_location?(library, location)
38✔
61
    end
62

63
    # @note This is called type because item_type is the value used in the
64
    #   /items endpoint. In migrating to Alma this is largely the policy value
65
    #   with a fallback.
66
    def type
1✔
67
      return 'Gen' if item_data['policy']['value'].blank?
2✔
68

UNCOV
69
      item_data['policy']['value']
×
70
    end
71

72
    # 876 field used for enrichment in
73
    # AlmaAdapter::MarcRecord#enrich_with_item
74
    def enrichment_876
1✔
75
      MARC::DataField.new(
6✔
76
        '876', ' ', ' ',
77
        *subfields_for_876
78
      )
79
    end
80

81
    def subfields_for_876
1✔
82
      [
83
        MARC::Subfield.new('0', holding_id),
6✔
84
        MARC::Subfield.new('3', enum_cron),
85
        MARC::Subfield.new('a', item_id),
86
        MARC::Subfield.new('p', barcode),
87
        MARC::Subfield.new('t', copy_number)
88
      ] + recap_876_fields
89
    end
90

91
    def recap_876_fields
1✔
92
      return [] unless recap_item?
6✔
93

94
      [
95
        MARC::Subfield.new('h', recap_use_restriction),
6✔
96
        MARC::Subfield.new('x', group_designation),
97
        MARC::Subfield.new('z', recap_customer_code),
98
        MARC::Subfield.new('j', recap_status),
99
        MARC::Subfield.new('l', 'RECAP'),
100
        MARC::Subfield.new('k', item.holding_library)
101
      ]
102
    end
103

104
    # Status isn't used for recap records, but 876j is a required field.
105
    def recap_status
1✔
106
      'Not Used'
7✔
107
    end
108

109
    def enum_cron
1✔
110
      return if enumeration.blank? && chronology.blank?
6✔
111
      return enumeration if chronology.blank?
1✔
112
      return chronology if enumeration.blank?
1✔
113

114
      "#{enumeration} (#{chronology})"
1✔
115
    end
116

117
    def enumeration
1✔
118
      enums = []
35✔
119
      enums << item.item_data['enumeration_a']
35✔
120
      enums << item.item_data['enumeration_b']
35✔
121
      enums << item.item_data['enumeration_c']
35✔
122
      enums << item.item_data['enumeration_d']
35✔
123
      enums << item.item_data['enumeration_e']
35✔
124
      enums << item.item_data['enumeration_f']
35✔
125
      enums << item.item_data['enumeration_g']
35✔
126
      enums << item.item_data['enumeration_h']
35✔
127
      enums.compact_blank.join(', ')
35✔
128
    end
129

130
    def chronology
1✔
131
      chrons = []
34✔
132
      chrons << item.item_data['chronology_i']
34✔
133
      chrons << item.item_data['chronology_j']
34✔
134
      chrons << item.item_data['chronology_k']
34✔
135
      chrons << item.item_data['chronology_l']
34✔
136
      chrons << item.item_data['chronology_m']
34✔
137
      chrons.compact_blank.join(', ')
34✔
138
    end
139

140
    def holding_id
1✔
141
      item.holding_data['holding_id']
46✔
142
    end
143

144
    def item_id
1✔
145
      item.item_data['pid']
8✔
146
    end
147

148
    def barcode
1✔
149
      item.item_data['barcode']
6✔
150
    end
151

152
    def copy_number
1✔
153
      item.holding_data['copy_id']
19✔
154
    end
155

156
    def call_number
1✔
157
      item.holding_data['call_number']
2✔
158
    end
159

160
    # def cdl?
161
    #   item.item_data.dig('work_order_type', 'value') == 'CDL'
162
    # end
163

164
    def recap_customer_code
1✔
165
      return unless recap_item?
8✔
166
      return 'PG' if item.location[0].casecmp('x').zero?
8✔
167

168
      item.location.upcase
7✔
169
    end
170

171
    def recap_use_restriction
1✔
172
      return unless recap_item?
31✔
173

174
      case item.location
31✔
175
      when *in_library_recap_groups
176
        'In Library Use'
8✔
177
      when *supervised_recap_groups
178
        'Supervised Use'
18✔
179
      end
180
    end
181

182
    def group_designation
1✔
183
      return unless recap_item?
77✔
184
      return 'Committed' if item_cgd_committed?
77✔
185

186
      case item.location
74✔
187
      when 'pv', 'pa', 'gp', 'qk', 'pf'
188
        'Shared'
20✔
189
      when *(in_library_recap_groups_and_private + supervised_recap_groups + no_access_recap_groups)
54✔
190
        'Private'
54✔
191
      end
192
    end
193

194
    def recap_item?
1✔
195
      all_recap_groups.include?(holding_location)
122✔
196
    end
197

198
    def all_recap_groups
1✔
199
      default_recap_groups +
122✔
200
        in_library_recap_groups +
201
        supervised_recap_groups +
202
        no_access_recap_groups
203
    end
204

205
    def default_recap_groups
1✔
206
      %w[pa gp qk pf]
122✔
207
    end
208

209
    def in_library_recap_groups
1✔
210
      in_library_recap_groups_and_shared + in_library_recap_groups_and_private
153✔
211
    end
212

213
    def in_library_recap_groups_and_shared
1✔
214
      ['pv']
153✔
215
    end
216

217
    def in_library_recap_groups_and_private
1✔
218
      %w[pj pk pl pm pn pt]
207✔
219
    end
220

221
    def supervised_recap_groups
1✔
222
      %w[pb ph ps pw pz xc xg xm xn xp xr xw xx xgr xcr phr xrr xmr]
199✔
223
    end
224

225
    def no_access_recap_groups
1✔
226
      %w[jq pe pg ph pq qb ql qv qx]
176✔
227
    end
228

229
    # Returns a JSON representation used for the /items endpoint.
230
    def as_json
1✔
231
      item['item_data'].merge(
2✔
232
        'id' => item_id,
233
        'copy_number' => copy_number.to_i,
234
        'temp_location' => composite_temp_location,
235
        'perm_location' => composite_perm_location,
236
        'item_type' => type
237
        # 'cdl' => cdl?
238
      )
239
    end
240

241
    def availability_summary
1✔
242
      status = calculate_status
27✔
243
      {
244
        barcode: item_data['barcode'],
27✔
245
        id: item_data['pid'],
246
        holding_id:,
247
        copy_number: holding_data['copy_id'],
248
        status: status[:code],        # Available
249
        status_label: status[:label], # Item in place
250
        status_source: status[:source], # e.g. work_order, process_type, base_status
251
        process_type: status[:process_type],
252
        on_reserve: on_reserve? ? 'Y' : 'N',
27✔
253
        item_type:, # e.g., Gen
254
        pickup_location_id: library, # firestone
255
        pickup_location_code: library, # firestone
256
        location: composite_location, # firestone$stacks
257
        label: holding_location_label(composite_location), # Firestore Library
258
        description: item_data['description'], # "v. 537, no. 7618 (2016 Sept. 1)" - new in Alma
259
        enum_display: enumeration, # in Alma there are many enumerations
260
        chron_display: chronology # in Alma there are many chronologies
261
      }.merge(temp_library_availability_summary)
262
    end
263

264
    def temp_library_availability_summary
1✔
265
      if in_temp_location?
27✔
266
        {
1✔
267
          in_temp_library: true,
268
          temp_library_code: temp_library,
269
          temp_library_label: holding_location_label(composite_temp_location),
270
          temp_location_code: composite_temp_location,
271
          temp_location_label: holding_location_label(composite_temp_location)
272
        }
273
      else
274
        { in_temp_library: false }
26✔
275
      end
276
    end
277

278
    def item_cgd_committed?
1✔
279
      item_committed_to_retain == 'true' && committed_retention_reasons.include?(item_retention_reason)
77✔
280
    end
281

282
    def committed_retention_reasons
1✔
283
      %w[ReCAPItalianImprints IPLCBrill ReCAPSACAP]
4✔
284
    end
285

286
    def item_retention_reason
1✔
287
      item_data.dig('retention_reason', 'value')
4✔
288
    end
289

290
    def item_committed_to_retain
1✔
291
      item_data.dig('committed_to_retain', 'value')
77✔
292
    end
293

294
    def item_type
1✔
295
      item_data.dig('policy', 'value')
27✔
296
    end
297

298
    def calculate_status
1✔
299
      return status_from_work_order_type if item_data.dig('work_order_type', 'value').present?
44✔
300
      return status_from_process_type if item_data.dig('process_type', 'value').present?
39✔
301

302
      status_from_base_status
30✔
303
    end
304

305
    def status_from_work_order_type
1✔
306
      value = item_data['work_order_type']['value']
5✔
307
      desc = item_data['work_order_type']['desc']
5✔
308

309
      # [Source for values](https://developers.exlibrisgroup.com/alma/apis/docs/xsd/rest_item.xsd/)
310
      # [Work Order documentation](https://pul-confluence.atlassian.net/wiki/spaces/ALMA/pages/1770142/Work+Orders)
311
      code = if value.in?(%w[Bind Pres AcqWorkOrder CollDev HMT])
5✔
312
               'Unavailable'
5✔
313
             else
314
               # "COURSE" or "PHYSICAL_TO_DIGITIZATION"
315
               'Available'
×
316
             end
317
      { code:, label: desc, source: 'work_order' }
5✔
318
    end
319

320
    def status_from_process_type
1✔
321
      # For now we return "Unavailable" for any item that has a process_type.
322
      # You can see a list of all the possible values here:
323
      #   https://developers.exlibrisgroup.com/alma/apis/docs/xsd/rest_item.xsd/
324
      value = item_data.dig('process_type', 'value')
9✔
325
      desc = item_data.dig('process_type', 'desc')
9✔
326

327
      { code: 'Unavailable', label: desc, source: 'process_type', process_type: value }
9✔
328
    end
329

330
    def status_from_base_status
1✔
331
      value = item_data.dig('base_status', 'value')
30✔
332
      desc = item_data.dig('base_status', 'desc')
30✔
333

334
      # Source for values: https://developers.exlibrisgroup.com/alma/apis/docs/xsd/rest_item.xsd/
335
      code = value == '1' ? 'Available' : 'Unavailable'
30✔
336
      { code:, label: desc, source: 'base_status' }
30✔
337
    end
338

339
    # Create the label by retrieving the value from the holding library label (external_name in Alma)
340
    # Add the library label in front if it exists
341
    def holding_location_label(code)
1✔
342
      label = HoldingLocation.find_by(code:)&.label
40✔
343
      [composite_library_label_display, label].select(&:present?).join(' - ')
40✔
344
    end
345
  end
346
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