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

Updated specRequestable logic #433

Open
wants to merge 34 commits into
base: production
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9851592
build nypl core loader
charmingduchess Dec 18, 2024
95e2b82
wip
charmingduchess Dec 19, 2024
e808c63
update async return
charmingduchess Dec 19, 2024
594f384
fix missing before bug
charmingduchess Dec 19, 2024
10d8b5a
invoke recap customer codes
charmingduchess Dec 19, 2024
cc785ce
rename nyplcore imports
charmingduchess Dec 19, 2024
0416945
add default for nypl core values
charmingduchess Dec 19, 2024
307d70b
rm argument from isUnfamiliarPatronType
charmingduchess Dec 19, 2024
3b3db65
update build spec requestable
charmingduchess Dec 19, 2024
0fbfb32
write tests for specRequestable
charmingduchess Dec 19, 2024
1293896
make collectionaccesstype explicit;
charmingduchess Dec 19, 2024
f0799f5
update nypl core objects version
charmingduchess Dec 19, 2024
d953e6e
update nypl core version
charmingduchess Dec 19, 2024
948fb18
rm console logs
charmingduchess Dec 19, 2024
d64a44e
update nyplcore version
charmingduchess Dec 19, 2024
ee0e34c
Merge branch 'noref-nypl-core-objects-async' into SCC-4286/update-spe…
charmingduchess Dec 19, 2024
76a75bc
account for variable casing of finding aid
charmingduchess Dec 19, 2024
aebc0b8
make spec mutually exclusive with edd and physRequestable
charmingduchess Dec 19, 2024
d64686e
comments and ternary update
charmingduchess Dec 20, 2024
ef7b80f
add spec requestable override criteria
charmingduchess Dec 20, 2024
7877170
update this with class name
charmingduchess Dec 20, 2024
ea70169
tests for phys requestable override
charmingduchess Dec 20, 2024
8b164e8
fix comment
charmingduchess Dec 20, 2024
b3447a8
rm console.log
charmingduchess Dec 20, 2024
c257110
Merge pull request #429 from NYPL/noref-nypl-core-objects-async
charmingduchess Dec 20, 2024
1a1ae1d
add specRequest override to eddRequestable
charmingduchess Dec 20, 2024
c367443
rm console.log
charmingduchess Dec 20, 2024
dfa867e
Merge pull request #430 from NYPL/SCC-4286/update-spec-requestable
charmingduchess Dec 20, 2024
7b79f1e
Merge pull request #431 from NYPL/noref-nypl-core-objects-async
charmingduchess Dec 20, 2024
41e7efb
update nypl core version
charmingduchess Jan 9, 2025
a26821b
Merge pull request #434 from NYPL/noref-update-nypl-core-rom-prom
charmingduchess Jan 10, 2025
58eda82
update nypl core objects
charmingduchess Jan 10, 2025
3ef787c
update nypl-core objects version
charmingduchess Jan 10, 2025
c658904
Merge pull request #436 from NYPL/noref-update-nypl-core-objects
charmingduchess Jan 10, 2025
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
3 changes: 2 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const express = require('express')
const esClient = require('./lib/elasticsearch/client')
const loadConfig = require('./lib/load-config')
const { preflightCheck } = require('./lib/preflight_check')
const { loadNyplCoreData } = require('./lib/load_nypl_core')

const swaggerDocs = require('./swagger.v1.1.x.json')

Expand All @@ -20,7 +21,7 @@ app.set('trust proxy', 'loopback')

app.init = async () => {
await loadConfig.loadConfig()

await loadNyplCoreData()
preflightCheck()

// Load logger after running above to ensure we respect LOG_LEVEL if set
Expand Down
2 changes: 1 addition & 1 deletion config/production.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ NYPL_OAUTH_URL=https://isso.nypl.org/
ENCRYPTED_NYPL_OAUTH_ID=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAGswaQYJKoZIhvcNAQcGoFwwWgIBADBVBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDMLKVUQA58B6vprNcAIBEIAoaz0lI9EL2M9NyTuEwT8JDmPBt6aXfMiFs027DEuwsCN0wS0qWeFL1g==
ENCRYPTED_NYPL_OAUTH_SECRET=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAIcwgYQGCSqGSIb3DQEHBqB3MHUCAQAwcAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyWz91LOP2YP5fg0q0CARCAQ9inO9SV1M8R0Pkkx84r7UdwlU1FxfXvIjk/z6Qs81KBAVELhby2iD5LawQyDrR9tjhuMbotS6QnydwwMR/p8+qJXHI=

NYPL_CORE_VERSION=v2.22
NYPL_CORE_VERSION=v2.23

LOG_LEVEL=info
FEATURES=on-site-edd
Expand Down
2 changes: 1 addition & 1 deletion config/qa.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ NYPL_OAUTH_URL=https://isso.nypl.org/
ENCRYPTED_NYPL_OAUTH_ID=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAGswaQYJKoZIhvcNAQcGoFwwWgIBADBVBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDMLKVUQA58B6vprNcAIBEIAoaz0lI9EL2M9NyTuEwT8JDmPBt6aXfMiFs027DEuwsCN0wS0qWeFL1g==
ENCRYPTED_NYPL_OAUTH_SECRET=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAIcwgYQGCSqGSIb3DQEHBqB3MHUCAQAwcAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyWz91LOP2YP5fg0q0CARCAQ9inO9SV1M8R0Pkkx84r7UdwlU1FxfXvIjk/z6Qs81KBAVELhby2iD5LawQyDrR9tjhuMbotS6QnydwwMR/p8+qJXHI=

NYPL_CORE_VERSION=v2.22
NYPL_CORE_VERSION=v2.23

LOG_LEVEL=info
FEATURES=on-site-edd
Expand Down
2 changes: 1 addition & 1 deletion config/test.env
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ NYPL_OAUTH_URL=http://oauth.example.com
ENCRYPTED_NYPL_OAUTH_ID=encrypted-nypl-oauth-id
ENCRYPTED_NYPL_OAUTH_SECRET=encrypted-nypl-oauth-id

NYPL_CORE_VERSION=v2.21
NYPL_CORE_VERSION=v2.23

LOG_LEVEL=error
FEATURES=on-site-edd
Expand Down
14 changes: 5 additions & 9 deletions lib/available_delivery_location_types.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const logger = require('./logger')
const { makeNyplDataApiClient } = require('./data-api-client')
const nyplCore = require('./load_nypl_core')

class AvailableDeliveryLocationTypes {
static getScholarRoomByPatronId (patronID) {
// If patronID is falsy (i.e. patron is not logged in) they're just a Rearcher:
if (!patronID) return Promise.resolve(['Research'])

const patronTypeMapping = require('@nypl/nypl-core-objects')('by-patron-type')
return this._getPatronTypeOf(patronID)
.then((patronType) => {
if (this._isUnfamiliarPatronType(patronTypeMapping, patronType)) {
if (this._isUnfamiliarPatronType(patronType)) {
return
}
const patronTypeData = patronTypeMapping[patronType]
const patronTypeData = nyplCore.patronTypes()[patronType]
return patronTypeData.scholarRoom && patronTypeData.scholarRoom.code
})
}
Expand All @@ -38,8 +38,8 @@ class AvailableDeliveryLocationTypes {
})
}

static _isUnfamiliarPatronType (patronTypeMapping, patronType) {
if (!patronTypeMapping[patronType]) {
static _isUnfamiliarPatronType (patronType) {
if (!nyplCore.patronTypes()[patronType]) {
logger.info(`Found the Patron Type: ${patronType} is not recognizable.`)
return true
} else {
Expand All @@ -48,8 +48,4 @@ class AvailableDeliveryLocationTypes {
}
}

const patronTypeMapping = require('@nypl/nypl-core-objects')('by-patron-type')

AvailableDeliveryLocationTypes.patronTypeMapping = patronTypeMapping

module.exports = AvailableDeliveryLocationTypes
60 changes: 27 additions & 33 deletions lib/delivery-locations-resolver.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const { itemHasRecapHoldingLocation, barcodeFromItem } = require('./util')
const scsbClient = require('./scsb-client')
const recapCustomerCodes = require('@nypl/nypl-core-objects')('by-recap-customer-code')
const sierraLocations = require('@nypl/nypl-core-objects')('by-sierra-location')
const nyplCore = require('./load_nypl_core')

const logger = require('./logger')
const onsiteEddCriteria = require('../data/onsite-edd-criteria.json')
const { isItemNyplOwned } = require('./ownership_determination')

class DeliveryLocationsResolver {
static nyplCoreLocation (locationCode) {
return sierraLocations[locationCode]
return nyplCore.sierraLocations()[locationCode]
}

static requestableBasedOnHoldingLocation (item) {
const locationCode = this.extractLocationCode(item)
const locationCode = DeliveryLocationsResolver.extractLocationCode(item)

if (!DeliveryLocationsResolver.nyplCoreLocation(locationCode)) {
logger.warn(`DeliveryLocationsResolver: Unrecognized holdingLocation for ${item.uri}: ${locationCode}`)
Expand Down Expand Up @@ -50,21 +50,15 @@ class DeliveryLocationsResolver {

// Fetch Sierra delivery locations by recap code
static deliveryLocationsByRecapCustomerCode (customerCode) {
if (recapCustomerCodes[customerCode] && recapCustomerCodes[customerCode].sierraDeliveryLocations) {
return recapCustomerCodes[customerCode].sierraDeliveryLocations
if (nyplCore.recapCustomerCodes()[customerCode] && nyplCore.recapCustomerCodes()[customerCode].sierraDeliveryLocations) {
return nyplCore.recapCustomerCodes()[customerCode].sierraDeliveryLocations
}
}

// Fetch Sierra delivery locations by m2 customer code. Returns undefined if the m2 customer code is not requestable:
static deliveryLocationsByM2CustomerCode (customerCode) {
let m2CustomerCodes
try {
m2CustomerCodes = require('@nypl/nypl-core-objects')('by-m2-customer-code')
} catch (e) {

}
if (m2CustomerCodes && m2CustomerCodes[customerCode] && m2CustomerCodes[customerCode].sierraDeliveryLocations) {
const { sierraDeliveryLocations, requestable } = m2CustomerCodes[customerCode]
if (nyplCore.m2CustomerCodes()?.[customerCode]?.sierraDeliveryLocations) {
const { sierraDeliveryLocations, requestable } = nyplCore.m2CustomerCodes()[customerCode]
if (requestable) {
return sierraDeliveryLocations
} else return undefined
Expand All @@ -73,7 +67,7 @@ class DeliveryLocationsResolver {

// Determine eddRequestable by recap customer code:
static __eddRequestableByCustomerCode (customerCode) {
if (recapCustomerCodes[customerCode]) return Boolean(recapCustomerCodes[customerCode].eddRequestable)
if (nyplCore.recapCustomerCodes()[customerCode]) return Boolean(nyplCore.recapCustomerCodes()[customerCode].eddRequestable)
}

// Determine eddRequestable by on-site EDD requestability criteria (presumed on-site):
Expand Down Expand Up @@ -172,7 +166,7 @@ class DeliveryLocationsResolver {
return {
id: `loc:${location.code}`,
label: location.label,
sortPosition: this.sortPosition(location)
sortPosition: DeliveryLocationsResolver.sortPosition(location)
}
})
// Either way, sort deliveryLocation entries by name:
Expand All @@ -197,14 +191,14 @@ class DeliveryLocationsResolver {
}

static attachRecapDeliveryInfo (item) {
const info = this.getRecapDeliveryInfo(item)
const info = DeliveryLocationsResolver.getRecapDeliveryInfo(item)
item.eddRequestable = info.eddRequestable
item.deliveryLocation = info.deliveryLocation
return item
}

static attachOnsiteDeliveryInfo (item) {
const info = this.getOnsiteDeliveryInfo(item)
const info = DeliveryLocationsResolver.getOnsiteDeliveryInfo(item)
item.eddRequestable = info.eddRequestable
item.deliveryLocation = info.deliveryLocation
return item
Expand All @@ -218,15 +212,15 @@ class DeliveryLocationsResolver {
const hasRecapCustomerCode = item.recapCustomerCode && item.recapCustomerCode[0]
const nyplItem = isItemNyplOwned(item)
if (!hasRecapCustomerCode) {
const requestableBasedOnHoldingLocation = nyplItem ? this.requestableBasedOnHoldingLocation(item) : true
const requestableBasedOnHoldingLocation = nyplItem ? DeliveryLocationsResolver.requestableBasedOnHoldingLocation(item) : true
// the length of the list of delivery locations is checked later to determine physical requestability
// In case of an offsite item with no recap customer code, we want this to be based on holding location
// so we put a placeholder '' in case it is requestable based on holding location
deliveryLocation = requestableBasedOnHoldingLocation ? [''] : []
eddRequestable = requestableBasedOnHoldingLocation
} else if (!nyplItem || this.requestableBasedOnHoldingLocation(item)) {
deliveryLocation = this.deliveryLocationsByRecapCustomerCode(item.recapCustomerCode[0])
eddRequestable = this.__eddRequestableByCustomerCode(item.recapCustomerCode[0])
} else if (!nyplItem || DeliveryLocationsResolver.requestableBasedOnHoldingLocation(item)) {
deliveryLocation = DeliveryLocationsResolver.deliveryLocationsByRecapCustomerCode(item.recapCustomerCode[0])
eddRequestable = DeliveryLocationsResolver.__eddRequestableByCustomerCode(item.recapCustomerCode[0])
} else {
deliveryLocation = []
eddRequestable = false
Expand All @@ -239,7 +233,7 @@ class DeliveryLocationsResolver {
eddRequestable: false,
deliveryLocation: []
}
const holdingLocationCode = this.extractLocationCode(item)
const holdingLocationCode = DeliveryLocationsResolver.extractLocationCode(item)
const sierraData = DeliveryLocationsResolver.nyplCoreLocation(holdingLocationCode)
if (!sierraData) {
// This case is mainly to satisfy a test which wants eddRequestable = false
Expand All @@ -249,15 +243,15 @@ class DeliveryLocationsResolver {
}
// if nypl core says it's unrequestable, it can still be eddRequestable,
// but its definitely not phys requestable.
deliveryInfo.eddRequestable = this.eddRequestableByOnSiteCriteria(item)
if (!this.requestableBasedOnHoldingLocation(item)) {
deliveryInfo.eddRequestable = DeliveryLocationsResolver.eddRequestableByOnSiteCriteria(item)
if (!DeliveryLocationsResolver.requestableBasedOnHoldingLocation(item)) {
return deliveryInfo
}
// if nypl-core reports that a holding location's delivery locations
// should be found by M2 code, but only if the item has an M2 customer code
const deliverableToResolution = sierraData.deliverableToResolution
if (deliverableToResolution === 'm2-customer-code' && item.m2CustomerCode && item.m2CustomerCode[0]) {
deliveryInfo.deliveryLocation = this.deliveryLocationsByM2CustomerCode(item.m2CustomerCode[0])
deliveryInfo.deliveryLocation = DeliveryLocationsResolver.deliveryLocationsByM2CustomerCode(item.m2CustomerCode[0])
}
// if no value, default to sierra location lookup
if (!deliverableToResolution) {
Expand All @@ -281,15 +275,15 @@ class DeliveryLocationsResolver {
}

/**
* Given an array of items (ES hits), returns the same items with `eddRequestable` & `deliveryLocations`
* Given an array of items (ES hits), returns the same items with `eddRequestable` & `deliveryLocations`. We verify all recap customer codes because our indexed data may be stale.
*
* @return Promise<Array<items>> A Promise that resolves and array of items, modified to include `eddRequestable` & `deliveryLocations`
*/
static attachDeliveryLocationsAndEddRequestability (items, scholarRoom) {
// Extract ReCAP barcodes from items:
const recapBarcodes = this.extractRecapBarcodes(items)
const recapBarcodes = DeliveryLocationsResolver.extractRecapBarcodes(items)
// Get a map from barcodes to ReCAP customercodes:
return this.__recapCustomerCodesByBarcodes(recapBarcodes)
return DeliveryLocationsResolver.__recapCustomerCodesByBarcodes(recapBarcodes)
.then((barcodeToRecapCustomerCode) => {
// Now map over items to affix deliveryLocations:
return items.map((item) => {
Expand All @@ -298,15 +292,15 @@ class DeliveryLocationsResolver {
item.recapCustomerCode = [barcodeToRecapCustomerCode[barcode]]
// If recap has a customer code for this barcode, map it by recap cust code:
if (item.recapCustomerCode[0]) {
item = this.attachRecapDeliveryInfo(item)
item = DeliveryLocationsResolver.attachRecapDeliveryInfo(item)
// Otherwise, it's an onsite item
} else {
item = this.attachOnsiteDeliveryInfo(item)
item = DeliveryLocationsResolver.attachOnsiteDeliveryInfo(item)
}
// Establish default for Electronic Document Delivery flag:
item.eddRequestable = !!item.eddRequestable
const filteredDeliveryLocationsWithScholarRoom = this.filterLocations(item.deliveryLocation, scholarRoom)
item.deliveryLocation = this.formatLocations(filteredDeliveryLocationsWithScholarRoom)
const filteredDeliveryLocationsWithScholarRoom = DeliveryLocationsResolver.filterLocations(item.deliveryLocation, scholarRoom)
item.deliveryLocation = DeliveryLocationsResolver.formatLocations(filteredDeliveryLocationsWithScholarRoom)
return item
})
})
Expand Down
9 changes: 4 additions & 5 deletions lib/jsonld_serializers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict'

const locations = require('@nypl/nypl-core-objects')('by-sierra-location')
const recordTypes = require('@nypl/nypl-core-objects')('by-record-types')
const nyplCore = require('./load_nypl_core')
const NyplSourceMapper = require('research-catalog-indexer/lib/utils/nypl-source-mapper')

const util = require('./util.js')
Expand Down Expand Up @@ -287,7 +286,7 @@ class ResourceSerializer extends JsonLdItemSerializer {
}

ResourceSerializer.getFormattedRecordType = function (recordTypeId) {
const prefLabel = recordTypes[recordTypeId]?.label
const prefLabel = nyplCore.recordTypes()[recordTypeId]?.label
if (!prefLabel) return null
return {
'@id': recordTypeId,
Expand Down Expand Up @@ -499,10 +498,10 @@ class AggregationSerializer extends JsonLdItemSerializer {
v.label = p[1]
} else if (field === 'buildingLocation') {
// Build buildingLocation agg labels from nypl-core:
v.label = locations[v.value]?.label
v.label = nyplCore.sierraLocations()[v.value]?.label
} else if (field === 'recordType') {
// Build recordType agg labels from nypl-core:
v.label = recordTypes[v.value]?.label
v.label = nyplCore.recordTypes()[v.value]?.label
// Unknown recordType? Remove it:
if (!v.label) return null
} else {
Expand Down
25 changes: 25 additions & 0 deletions lib/load_nypl_core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const _data = {}
const nyplCoreObjects = require('@nypl/nypl-core-objects')

const loadNyplCoreData = () => {
const vocabularies = {
sierraLocations: 'by-sierra-location',
recordTypes: 'by-record-types',
recapCustomerCodes: 'by-recap-customer-code',
m2CustomerCodes: 'by-m2-customer-code',
patronTypes: 'by-patron-type'
}
return Promise.all(Object.keys(vocabularies).map(async (vocab) => {
const nyplCoreValues = await nyplCoreObjects(vocabularies[vocab])
_data[vocab] = nyplCoreValues
}))
}

module.exports = {
loadNyplCoreData,
patronTypes: () => _data.patronTypes || {},
sierraLocations: () => _data.sierraLocations || {},
recapCustomerCodes: () => _data.recapCustomerCodes || {},
recordTypes: () => _data.recordTypes || {},
m2CustomerCodes: () => _data.m2CustomerCodes || {}
}
12 changes: 7 additions & 5 deletions lib/location_label_updater.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const sierraLocations = require('@nypl/nypl-core-objects')('by-sierra-location')
const nyplCore = require('./load_nypl_core')

class LocationLabelUpdater {
constructor (responseReceived) {
Expand All @@ -10,20 +10,22 @@ class LocationLabelUpdater {
const resp = this.elasticSearchResponse
const updatedHits = resp.hits.hits.map((bib) => {
// Update locations for items:
; (bib._source.items || []).forEach((item) => {
const items = bib._source.items || []
items.forEach((item) => {
if (item.holdingLocation && item.holdingLocation.length > 0) {
item.holdingLocation = item.holdingLocation.map((loc) => {
const nyplCoreEntry = sierraLocations[loc.id.replace(/^loc:/, '')]
const nyplCoreEntry = nyplCore.sierraLocations()[loc.id.replace(/^loc:/, '')]
if (nyplCoreEntry) loc.label = nyplCoreEntry.label
return loc
})
}
})
// Update locations for holdings:
; (bib._source.holdings || []).forEach((holding) => {
const holdings = bib._source.holdings || []
holdings.forEach((holding) => {
if (holding.location && holding.location.length > 0) {
holding.location = holding.location.map((loc) => {
const nyplCoreEntry = sierraLocations[loc.code.replace(/^loc:/, '')]
const nyplCoreEntry = nyplCore.sierraLocations()[loc.code.replace(/^loc:/, '')]
if (nyplCoreEntry) loc.label = nyplCoreEntry.label
return loc
})
Expand Down
Loading
Loading