Skip to content

Commit 6de9fc0

Browse files
committed
Adjust fulfillment link logic
Why are these changes being introduced: * We prefer LibKey links when available, but they are not always available. Relevant ticket(s): * https://mitlibraries.atlassian.net/browse/USE-286 How does this address that need: * Adjusts links we extract from Primo to include HTML and PDF direct links when available. * Updates Primo OpenURL logic and adjust label to "Check Availability" * If LibKey links become available, they will be used instead of the direct links from Primo by hiding them from the UI. Document any side effects to this change: * Hiding links after they are in the UI is not ideal, but it is a workaround to ensure that we do not display duplicate links in the UI. This will be revisited when we have more time. * Introduced a new required ENV variable `PRIMO_INST_ID`. This was previously named differently and not included in the readme or deployed applications so our OpenURL links we broken.
1 parent c5db4e3 commit 6de9fc0

File tree

8 files changed

+114
-14
lines changed

8 files changed

+114
-14
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ See `Optional Environment Variables` for more information.
7979
- `MIT_PRIMO_URL`: The base URL for MIT Libraries' Primo instance (used to generate record links).
8080
- `PRIMO_API_KEY`: The Primo Search API key.
8181
- `PRIMO_API_URL`: The Primo Search API base URL.
82+
- `PRIMO_INST_ID`: Our Ex Libris Instituion ID
8283
- `PRIMO_SCOPE`: The Primo Search API `scope` used in Primo UI links. Does not affect our API calls. Ask Enterprise Systems for value.
8384
- `PRIMO_TAB`: The Primo Search API `tab` used in Primo UI links. Does not affect our API calls. Ask Enterprise Systems for value.
8485
- `PRIMO_VID`: The Primo Search API `vid` (or 'view ID`) param. Used in both our API calls and Primo UI. Ask Enterprise Systems for value.

app/javascript/controllers/content_loader_controller.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ export default class extends Controller {
1010
load() {
1111
fetch(this.urlValue)
1212
.then(response => response.text())
13-
.then(html => this.element.innerHTML = html)
13+
.then(html => {
14+
this.element.innerHTML = html;
15+
// Hide primo links if libkey link is present
16+
if (this.element.querySelector('.libkey-link')) {
17+
const resultGet = this.element.closest('.result-get');
18+
if (resultGet) {
19+
const primoLinks = resultGet.querySelectorAll('.primo-link');
20+
primoLinks.forEach(link => link.style.display = 'none');
21+
}
22+
}
23+
})
1424
}
1525
}

app/models/libkey.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def self.lookup(type:, identifier:, libkey_client: nil)
2929
Sentry.set_tags('mitlib.libkeyurl': url)
3030
Sentry.set_tags('mitlib.libkeystatus': e.message)
3131
Sentry.capture_message('Unexpected Libkey response status')
32+
Rails.logger.error("Unexpected Libkey response status: #{e.message}")
3233
nil
3334
rescue HTTP::Error
3435
Rails.logger.error('Libkey connection error')

app/models/normalize_primo_record.rb

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,55 @@ def links
107107
links << { 'url' => record_link, 'kind' => 'full record' }
108108
end
109109

110-
# Add openurl if available
111-
links << { 'url' => openurl, 'kind' => 'openurl' } if openurl.present?
110+
# If $$Topenurl_article
111+
if @record['pnx']['links'] && @record['pnx']['links']['openurl'] && openurl.present?
112+
links << { 'url' => openurl, 'kind' => 'Check Availability' }
113+
end
114+
115+
# Add PDF if available
116+
if @record['pnx']['links'] && @record['pnx']['links']['linktopdf']
117+
118+
parsed_string = parse_link_string(@record['pnx']['links']['linktopdf'].first)
119+
120+
if parsed_string['U'].present?
121+
links << { 'url' => parsed_string['U'],
122+
'kind' => 'View PDF' }
123+
end
124+
end
125+
126+
# Add HTML if available
127+
if @record['pnx']['links'] && @record['pnx']['links']['linktohtml']
128+
129+
parsed_string = parse_link_string(@record['pnx']['links']['linktohtml'].first)
130+
131+
if parsed_string['U'].present?
132+
links << { 'url' => parsed_string['U'],
133+
'kind' => 'View HTML' }
134+
end
135+
end
112136

113137
# Return links if we found any
114138
links.any? ? links : []
115139
end
116140

141+
# Parses a link string into a hash of key-value pairs.
142+
# The link string is a series of key-value pairs separated by $$, where each pair is prefixed by a single character.
143+
# For example: "$$Uhttps://example.com$$TView PDF" would be parsed into { 'U' => 'https://example.com', 'T' => 'View PDF' }
144+
def parse_link_string(link_string)
145+
return unless link_string.start_with?('$$')
146+
147+
parts = link_string.split('$$')
148+
hash = {}
149+
parts.each do |part|
150+
next if part.empty?
151+
152+
key = part[0]
153+
value = part[1..-1]
154+
hash[key] = value
155+
end
156+
hash
157+
end
158+
117159
def citation
118160
return unless @record['pnx']['display']
119161

@@ -260,22 +302,26 @@ def openurl
260302

261303
def construct_primo_openurl
262304
return unless @record['delivery']['almaOpenurl']
305+
# Primo OpenURL links to some formats are not helpful
306+
return if format == 'Journal'
307+
return if format == 'Video'
263308

264309
# Here we are converting the Alma link resolver URL provided by the Primo
265310
# Search API to redirect to the Primo UI. This is done for UX purposes,
266311
# as the regular Alma link resolver URLs redirect to a plaintext
267312
# disambiguation page.
268313
primo_openurl_base = [ENV.fetch('MIT_PRIMO_URL', nil),
269314
'/discovery/openurl?institution=',
270-
ENV.fetch('EXL_INST_ID', nil),
315+
ENV.fetch('PRIMO_INST_ID', nil),
271316
'&vid=',
272317
ENV.fetch('PRIMO_VID', nil),
273318
'&'].join
274319
primo_openurl = @record['delivery']['almaOpenurl'].gsub(ENV.fetch('ALMA_OPENURL', nil), primo_openurl_base)
275320

276321
# The ctx params appear to break Primo openurls, so we need to remove them.
277-
params = Rack::Utils.parse_nested_query(primo_openurl)
278-
filtered = params.delete_if { |key, _value| key.starts_with?('ctx') }
322+
# params = Rack::Utils.parse_nested_query(primo_openurl)
323+
# filtered = params.delete_if { |key, _value| key.starts_with?('ctx') }
324+
filtered = primo_openurl
279325
URI::DEFAULT_PARSER.unescape(filtered.to_param)
280326
end
281327

app/views/libkey/lookup.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<% if Libkey.enabled? && @libkey.present? %>
2-
<%= link_to( @libkey[:text], @libkey[:link], class: 'button', style: 'border: none;' ) %>
2+
<%= link_to( @libkey[:text], @libkey[:link], class: 'button libkey-link' ) %>
33
<% end %>

app/views/search/_result_primo.html.erb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
</h3>
1414

1515
<div class="result-metadata">
16-
1716
<p class="pub-info">
1817
<span><%= result[:format] %></span>
1918
<span><%= result[:year] %></span>
@@ -63,19 +62,25 @@
6362
<% if result[:links].present? %>
6463
<% result[:links].each do |link| %>
6564
<% if link['kind'].downcase == 'full record' %>
65+
<%# Full record link button uses the same link as the title link %>
6666
<% if Feature.enabled?(:record_link) %>
6767
<%= link_to 'View full record', link['url'], class: 'button' %>
6868
<% end %>
69+
<%# Primo supplies PDF and HTML links in addition to the OpenURL variant that may be useful. %>
70+
<%# We hide links with the `primo-link` css class if LibKey returns results. %>
6971
<% else %>
70-
<%= link_to link['kind'].titleize, link['url'], class: 'button' %>
72+
<%= link_to link['kind'], link['url'], class: 'button primo-link' %>
7173
<% end %>
7274
<% end %>
7375
<% end %>
76+
77+
<%# LibKey links are our preferred links so we call the API when possible to get these preferred links %>
7478
<% if Libkey.enabled? && result[:doi].present? %>
7579
<%= render(partial: 'trigger_libkey', locals: { kind: 'doi', identifier: result[:doi] }) %>
7680
<% elsif Libkey.enabled? && result[:pmid].present? %>
7781
<%= render(partial: 'trigger_libkey', locals: { kind: 'pmid', identifier: result[:pmid] }) %>
7882
<% end %>
83+
7984
<% if result[:availability].present? %>
8085
<span class="availability"><%= availability(result[:availability], result[:location], result[:other_availability]) %></span>
8186
<% end %>

test/fixtures/primo/full_record.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,26 @@
6060
"22110403"
6161
]
6262
},
63+
"links": {
64+
"openurl": [
65+
"$$Topenurl_article"
66+
],
67+
"openurlfulltext": [
68+
"$$Topenurlfull_article"
69+
],
70+
"backlink": [
71+
"$$Uhttp://pascal-francis.inist.fr/vibad/index.php?action=getRecordDetail&idt=21178602$$DView record in Pascal Francis$$Hfree_for_read"
72+
],
73+
"thumbnail": [
74+
"$$Tsyndetics_thumb_exl"
75+
],
76+
"linktopdf": [
77+
"$$Uhttps://libproxy.mit.edu/login?&url=https://www.jstor.org/stable/pdf/20464433$$EPDF$$P50$$Gjstor$$H"
78+
],
79+
"linktohtml": [
80+
"$$Uhttps://libproxy.mit.edu/login?&url=https://www.jstor.org/stable/20464433$$EHTML$$P50$$Gjstor$$H"
81+
]
82+
},
6383
"facets": {
6484
"frbrtype": [
6585
"5"

test/models/normalize_primo_record_test.rb

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,29 @@ def cdi_record
7373
assert_equal 'full record', normalized[:links].first['kind']
7474

7575
# Second link should be the Alma openurl
76-
assert_equal 'openurl', normalized[:links].second['kind']
76+
assert_equal 'Check Availability', normalized[:links].second['kind']
7777
end
7878

7979
test 'handles missing links' do
8080
normalized = NormalizePrimoRecord.new(minimal_record, 'test').normalize
8181
assert_empty normalized[:links]
8282
end
8383

84+
test 'parse_link_string creates expected data structure' do
85+
# Strings that don't start with $$ should not be processed
86+
link_string = 'https://example.com?param1=value1&param2=value2'
87+
assert_nil NormalizePrimoRecord.new(full_record, 'test').send(:parse_link_string, link_string)
88+
89+
# Extract properly formatted links
90+
link_string = '$$Uhttps://libproxy.mit.edu/login?&url=https://www.jstor.org/stable/pdf/20464433$$EPDF$$P50$$Gjstor$$H'
91+
expected = { 'U' => 'https://libproxy.mit.edu/login?&url=https://www.jstor.org/stable/pdf/20464433', 'E' => 'PDF', 'P' => '50', 'G' => 'jstor', 'H' => '' }
92+
assert_equal expected, NormalizePrimoRecord.new(full_record, 'test').send(:parse_link_string, link_string)
93+
94+
link_string = '$$Uhttps://libproxy.mit.edu/login?&url=https://www.jstor.org/stable/20464433$$EHTML$$P50$$Gjstor$$H'
95+
expected = { 'U' => 'https://libproxy.mit.edu/login?&url=https://www.jstor.org/stable/20464433', 'E' => 'HTML', 'P' => '50', 'G' => 'jstor', 'H' => '' }
96+
assert_equal expected, NormalizePrimoRecord.new(full_record, 'test').send(:parse_link_string, link_string)
97+
end
98+
8499
test 'normalizes citation' do
85100
normalized = NormalizePrimoRecord.new(full_record, 'test').normalize
86101
assert_equal 'Journal of Testing, Vol. 2, Issue 3', normalized[:citation]
@@ -278,8 +293,10 @@ def cdi_record
278293
link_kinds = normalized[:links].map { |link| link['kind'] }
279294

280295
assert_includes link_kinds, 'full record'
281-
assert_includes link_kinds, 'openurl'
282-
assert_equal 2, normalized[:links].length # Only full record and openurl
296+
assert_includes link_kinds, 'Check Availability'
297+
assert_includes link_kinds, 'View PDF'
298+
assert_includes link_kinds, 'View HTML'
299+
assert_equal 4, normalized[:links].length
283300
end
284301

285302
# Additional coverage tests for existing methods
@@ -351,14 +368,14 @@ def cdi_record
351368
test 'handles openurl server validation' do
352369
# Test with matching server
353370
normalized = NormalizePrimoRecord.new(full_record, 'test').normalize
354-
openurl_link = normalized[:links].find { |link| link['kind'] == 'openurl' }
371+
openurl_link = normalized[:links].find { |link| link['kind'] == 'Check Availability' }
355372
assert_not_nil openurl_link
356373

357374
# Test with mismatched server. Should log warning but still return URL
358375
record = full_record.deep_dup
359376
record['delivery']['almaOpenurl'] = 'https://different.server.com/openurl?param=value'
360377
normalized = NormalizePrimoRecord.new(record, 'test').normalize
361-
openurl_link = normalized[:links].find { |link| link['kind'] == 'openurl' }
378+
openurl_link = normalized[:links].find { |link| link['kind'] == 'Check Availability' }
362379
assert_not_nil openurl_link
363380
end
364381

0 commit comments

Comments
 (0)