Skip to content

Commit a584d35

Browse files
authored
Merge pull request #322 from MITLibraries/use-286-openurl
Adjust fulfillment link logic
2 parents eb555b4 + adae97f commit a584d35

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
# We don't want to include citations for Alma records at this time. If we include them in the future they need
119161
# to be cleaned up as they currently look like `Engineering village 2$$QEngineering village 2`
@@ -263,22 +305,26 @@ def openurl
263305

264306
def construct_primo_openurl
265307
return unless @record['delivery']['almaOpenurl']
308+
# Primo OpenURL links to some formats are not helpful
309+
return if format == 'Journal'
310+
return if format == 'Video'
266311

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

279324
# The ctx params appear to break Primo openurls, so we need to remove them.
280-
params = Rack::Utils.parse_nested_query(primo_openurl)
281-
filtered = params.delete_if { |key, _value| key.starts_with?('ctx') }
325+
# params = Rack::Utils.parse_nested_query(primo_openurl)
326+
# filtered = params.delete_if { |key, _value| key.starts_with?('ctx') }
327+
filtered = primo_openurl
282328
URI::DEFAULT_PARSER.unescape(filtered.to_param)
283329
end
284330

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 for cdi records' do
85100
record = full_record.dup
86101
record['pnx']['control']['recordid'] = ['cdi_crossref_primary_10_1234_test_article']
@@ -286,8 +301,10 @@ def cdi_record
286301
link_kinds = normalized[:links].map { |link| link['kind'] }
287302

288303
assert_includes link_kinds, 'full record'
289-
assert_includes link_kinds, 'openurl'
290-
assert_equal 2, normalized[:links].length # Only full record and openurl
304+
assert_includes link_kinds, 'Check Availability'
305+
assert_includes link_kinds, 'View PDF'
306+
assert_includes link_kinds, 'View HTML'
307+
assert_equal 4, normalized[:links].length
291308
end
292309

293310
# Additional coverage tests for existing methods
@@ -359,14 +376,14 @@ def cdi_record
359376
test 'handles openurl server validation' do
360377
# Test with matching server
361378
normalized = NormalizePrimoRecord.new(full_record, 'test').normalize
362-
openurl_link = normalized[:links].find { |link| link['kind'] == 'openurl' }
379+
openurl_link = normalized[:links].find { |link| link['kind'] == 'Check Availability' }
363380
assert_not_nil openurl_link
364381

365382
# Test with mismatched server. Should log warning but still return URL
366383
record = full_record.deep_dup
367384
record['delivery']['almaOpenurl'] = 'https://different.server.com/openurl?param=value'
368385
normalized = NormalizePrimoRecord.new(record, 'test').normalize
369-
openurl_link = normalized[:links].find { |link| link['kind'] == 'openurl' }
386+
openurl_link = normalized[:links].find { |link| link['kind'] == 'Check Availability' }
370387
assert_not_nil openurl_link
371388
end
372389

0 commit comments

Comments
 (0)