Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set current location from request service point for 'awaiting pickup' items #1020

Merged
merged 3 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/boot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
require 'folio_record'
require 'folio/eresource_holdings_builder'
require 'folio/mhld_builder'
require 'folio/status_current_location'
require 'locations_map'
require 'marc_links'
require 'mhld_field'
Expand Down
57 changes: 57 additions & 0 deletions lib/folio/status_current_location.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

module Folio
# Folio::StatusCurrentLocation takes an item
# and any requests associated with the item's instance record
# and creates an equivalent Symphony current location
class StatusCurrentLocation
attr_reader :item

def initialize(item)
@item = item
end

def current_location
case status
when 'Checked out', 'Claimed returned', 'Aged to lost'
'CHECKEDOUT'
when 'Awaiting pickup'
symphony_pickup_location_code
when 'In process', 'In process (non-requestable)'
'INPROCESS'
when 'In transit', 'Awaiting delivery'
'INTRANSIT'
when 'Missing', 'Long missing'
'MISSING'
when 'On order'
'ON-ORDER'
end
end

private

def status
item['status']
end

# ARS-LOAN, RUM-LOAN, and SPE-LOAN each receives a
# special label in SearchWorks. Other codes are generically
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where will these labels come from in Searchworks? Aren't we moving toward using Folio location labels?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The equivalent in FOLIO for these locations would be service points, which I don't think we're doing anything with in SearchWorks yet. We might want to. But this gets us to parity with Symphony by relying on the existing Constants:LOCS mappings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the intention is to remove Constants::LOCS after migrating to Folio, so we need to handle these in some other way. (see sul-dlss/SearchWorks#3310)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cbeer I'm confused about what the plan is for Constants::LOCS vs FOLIO display names. My guess is we're still going to have location codes that don't map to anything in FOLIO and we'll need to maintain a fallback list in SearchWorks (maybe I'm wrong about this). This PR is a refinement to previous work to map folio statuses to symphony locations so they'll display right in searchworks. Is this no longer what we want to do?

See:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to capture post-folio work related to this in #1026

# mapped to GRE-LOAN since they all receive the same label.
def symphony_pickup_location_code
case service_point_code
when 'ARS'
'ARS-LOAN'
when 'RUMSEY-MAP'
'RUM-LOAN'
when 'SPEC'
'SPE-LOAN'
else
'GRE-LOAN'
end
end

def service_point_code
item&.dig('request', 'pickupServicePoint', 'code')
end
end
end
21 changes: 2 additions & 19 deletions lib/folio_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def sirsi_holdings

library_code, home_location_code = LocationsMap.for(item_location_code)
_current_library, current_location = LocationsMap.for(item.dig('location', 'temporaryLocation', 'code'))
current_location ||= folio_status_to_location(item['status'])
current_location ||= Folio::StatusCurrentLocation.new(item).current_location

SirsiHolding.new(
id: item['id'],
Expand Down Expand Up @@ -114,7 +114,7 @@ def bound_with_holdings

library_code, home_location_code = LocationsMap.for(item_location_code)
_current_library, current_location = LocationsMap.for(parent_item.dig('location', 'temporaryLocation', 'code'))
current_location ||= folio_status_to_location(parent_item['status'])
current_location ||= Folio::StatusCurrentLocation.new(parent_item).current_location
SirsiHolding.new(
id: parent_item['id'],
call_number: holding['callNumber'],
Expand Down Expand Up @@ -343,22 +343,5 @@ def folio_electronic_access_marc_field(eresource)

MARC::DataField.new('856', '4', ind2, ['u', eresource['uri']], ['y', eresource['linkText']], ['z', eresource['publicNote']])
end

def folio_status_to_location(status)
case status
when 'Checked out', 'Claimed returned', 'Aged to lost'
'CHECKEDOUT'
when 'Awaiting pickup', 'Awaiting delivery'
'GRE-LOAN'
when 'In process', 'In process (non-requestable)'
'INPROCESS'
when 'In transit'
'INTRANSIT'
when 'Missing', 'Long missing'
'MISSING'
when 'On order'
'ON-ORDER'
end
end
end
# rubocop:enable Metrics/ClassLength
33 changes: 32 additions & 1 deletion lib/traject/readers/folio_postgres_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,24 @@ def locations
end
end

def service_points
@service_points ||= begin
response = @connection.exec <<-SQL
SELECT service_point.id AS id,
jsonb_build_object(
'id', service_point.id,
'code', service_point.jsonb ->> 'code',
'name', service_point.jsonb ->> 'name',
'pickupLocation', COALESCE((service_point.jsonb ->> 'pickupLocation')::bool, false),
'discoveryDisplayName', service_point.jsonb ->> 'discoveryDisplayName'
) AS jsonb
FROM sul_mod_inventory_storage.service_point service_point
SQL

response.map { |row| JSON.parse(row['jsonb']) }.index_by { |service_point| service_point['id'] }
end
end

def each
return to_enum(:each) unless block_given?

Expand Down Expand Up @@ -116,6 +134,8 @@ def each
'permanentLocation' => locations[item['permanentLocationId']],
'temporaryLocation' => locations[item['temporaryLocationId']]
}.compact

item['request']['pickupServicePoint'] = service_points[item['request']['pickupServicePointId']] if item['request']
end

data['holdings'].each do |holding|
Expand Down Expand Up @@ -187,7 +207,14 @@ def sql_query(conditions, addl_from: nil)
'electronicAccess', COALESCE(sul_mod_inventory_storage.getElectronicAccessName(COALESCE(item.jsonb #> '{electronicAccess}', '[]'::jsonb)), '[]'::jsonb),
'administrativeNotes', '[]'::jsonb,
'circulationNotes', COALESCE((SELECT jsonb_agg(e) FROM jsonb_array_elements(item.jsonb -> 'circulationNotes') AS e WHERE NOT COALESCE((e ->> 'staffOnly')::bool, false)), '[]'::jsonb),
'notes', COALESCE((SELECT jsonb_agg(e || jsonb_build_object('itemNoteTypeName', ( SELECT jsonb ->> 'name' FROM sul_mod_inventory_storage.item_note_type WHERE id = nullif(e ->> 'itemNoteTypeId','')::uuid ))) FROM jsonb_array_elements(item.jsonb -> 'notes') AS e WHERE NOT COALESCE((e ->> 'staffOnly')::bool, false)), '[]'::jsonb)
'notes', COALESCE((SELECT jsonb_agg(e || jsonb_build_object('itemNoteTypeName', ( SELECT jsonb ->> 'name' FROM sul_mod_inventory_storage.item_note_type WHERE id = nullif(e ->> 'itemNoteTypeId','')::uuid ))) FROM jsonb_array_elements(item.jsonb -> 'notes') AS e WHERE NOT COALESCE((e ->> 'staffOnly')::bool, false)), '[]'::jsonb),
'request', CASE WHEN request.id IS NOT NULL THEN
jsonb_build_object(
'id', request.id,
'status', request.jsonb ->> 'status',
'pickupServicePointId', request.jsonb ->> 'pickupServicePointId'
)
END
)
) FILTER (WHERE item.id IS NOT NULL),
'[]'::jsonb),
Expand Down Expand Up @@ -317,6 +344,10 @@ def sql_query(conditions, addl_from: nil)
ON parentItem.holdingsRecordId = parentHolding.id
LEFT JOIN sul_mod_inventory_storage.instance parentInstance
ON parentHolding.instanceid = parentInstance.id
-- Requests relation
LEFT JOIN sul_mod_circulation_storage.request request
ON (request.jsonb ->> 'itemId')::uuid = item.id
AND request.jsonb ->> 'status' = 'Open - Awaiting pickup'
#{addl_from}
WHERE #{conditions.join(' AND ')}
GROUP BY vi.id
Expand Down
129 changes: 129 additions & 0 deletions spec/integration/folio_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,135 @@

it { expect(result['item_display'].find { |h| h.match?(/INTRANSIT/) }).to be_present }
end

context 'item is awaiting pickup' do
let(:items) do
[{ 'id' => '7fdf7094-d30a-5f70-b23e-bc420a82a1d7',
'hrid' => 'ai645341_1_1',
'notes' => [],
'status' => 'Awaiting pickup',
'barcode' => '36105080746311',
'_version' => 3,
'request' => { 'id' => '7c8e3f57-6f1b-4d59-a8c6-9b51e32edd38',
'status' => 'Open - Awaiting pickup',
'pickupServicePoint' =>
{ 'pickupServicePointId' => 'b6987737-1e63-44cc-bfb1-2bcf044adcd7',
'code' => 'RUMSEY-MAP',
'name' => 'David Rumsey Map Center',
'pickupLocation' => true,
'discoveryDisplayName' => 'David Rumsey Map Center' } },
'location' =>
{ 'effectiveLocation' =>
{ 'id' => 'bb7bd5d2-5b97-4fc6-9dfd-b26a1c14e43f',
'code' => 'SAL-PAGE',
'name' => 'SAL Stacks',
'campus' =>
{ 'id' => 'c365047a-51f2-45ce-8601-e421ca3615c5',
'code' => 'SUL',
'name' => 'Stanford Libraries' },
'details' => { 'scanServicePointCode' => 'GREEN' },
'library' =>
{ 'id' => '00d012b4-d5ee-422c-9f38-3457e0ddd1ed',
'code' => 'SAL',
'name' => 'Stanford Auxiliary Library 1&2' },
'isActive' => true,
'institution' =>
{ 'id' => '8d433cdd-4e8f-4dc1-aa24-8a4ddb7dc929',
'code' => 'SU',
'name' => 'Stanford University' } },
'permanentLocation' =>
{ 'id' => 'bb7bd5d2-5b97-4fc6-9dfd-b26a1c14e43f',
'code' => 'SAL-PAGE',
'name' => 'SAL Stacks',
'campus' =>
{ 'id' => 'c365047a-51f2-45ce-8601-e421ca3615c5',
'code' => 'SUL',
'name' => 'Stanford Libraries' },
'details' => { 'scanServicePointCode' => 'GREEN' },
'library' =>
{ 'id' => '00d012b4-d5ee-422c-9f38-3457e0ddd1ed',
'code' => 'SAL',
'name' => 'Stanford Auxiliary Library 1&2' },
'isActive' => true,
'institution' =>
{ 'id' => '8d433cdd-4e8f-4dc1-aa24-8a4ddb7dc929',
'code' => 'SU',
'name' => 'Stanford University' } },
'temporaryLocation' => nil } }]
end
let(:holdings) do
[{ 'id' => '9c7b3dca-1619-5210-9bd1-6df775986b81',
'hrid' => 'ah645341_1',
'notes' => [],
'_version' => 1,
'location' =>
{ 'effectiveLocation' =>
{ 'id' => 'bb7bd5d2-5b97-4fc6-9dfd-b26a1c14e43f',
'code' => 'SAL-PAGE',
'name' => 'SAL Stacks',
'campus' =>
{ 'id' => 'c365047a-51f2-45ce-8601-e421ca3615c5',
'code' => 'SUL',
'name' => 'Stanford Libraries' },
'details' => { 'scanServicePointCode' => 'GREEN' },
'library' =>
{ 'id' => '00d012b4-d5ee-422c-9f38-3457e0ddd1ed',
'code' => 'SAL',
'name' => 'Stanford Auxiliary Library 1&2' },
'isActive' => true,
'institution' =>
{ 'id' => '8d433cdd-4e8f-4dc1-aa24-8a4ddb7dc929',
'code' => 'SU',
'name' => 'Stanford University' } },
'permanentLocation' =>
{ 'id' => 'bb7bd5d2-5b97-4fc6-9dfd-b26a1c14e43f',
'code' => 'SAL-PAGE',
'name' => 'SAL Stacks',
'campus' =>
{ 'id' => 'c365047a-51f2-45ce-8601-e421ca3615c5',
'code' => 'SUL',
'name' => 'Stanford Libraries' },
'details' => { 'scanServicePointCode' => 'GREEN' },
'library' =>
{ 'id' => '00d012b4-d5ee-422c-9f38-3457e0ddd1ed',
'code' => 'SAL',
'name' => 'Stanford Auxiliary Library 1&2' },
'isActive' => true,
'institution' =>
{ 'id' => '8d433cdd-4e8f-4dc1-aa24-8a4ddb7dc929',
'code' => 'SU',
'name' => 'Stanford University' } },
'temporaryLocation' => nil },
'formerIds' => [],
'callNumber' => 'D810.S8 C31 A32',
'instanceId' => 'c08db92b-c343-5955-abb1-b739ab186ecb',
'holdingsType' =>
{ 'id' => '03c9c400-b9e3-4a07-ac0e-05ab470233ed',
'name' => 'Monograph',
'source' => 'folio' },
'holdingsItems' => [],
'callNumberType' =>
{ 'id' => '95467209-6d7b-468b-94df-0f5d7ad2747d',
'name' => 'Library of Congress classification',
'source' => 'folio' },
'holdingsStatements' => [],
'suppressFromDiscovery' => false,
'holdingsStatementsForIndexes' => [],
'holdingsStatementsForSupplements' => [] }]
end
let(:items_and_holdings) do
{ 'items' => items,
'holdings' => holdings }
end

before do
allow(client).to receive(:pieces).and_return([])
end

it 'uses the pickup location of the request to generate a current location value' do
expect(result['item_display'].find { |h| h.match?(/RUM-LOAN/) }).to be_present
end
end
end

describe 'mhld_display' do
Expand Down