Skip to content

Commit cf4d71a

Browse files
committed
Refactor how TIMDEX records are nomalized
Why these changes are being introduced: Prior to USE, TIMDEX records were largely normalized in the view layer. While not ideal, this made some sense given that TIMDEX was the sole source we were working with in the UI. Now that we have introduced Primo results in USE, we should normalize TIMDEX records similarly, such that the two share a similar data structure. Relevant ticket(s): * [USE-73](https://mitlibraries.atlassian.net/browse/USE-73) How this addresses that need: This introduces NormalizeTimdexReuslts and NormalizeTimdexRecord models to parallel the Primo models. The normalization models share a structure to the extent that it is meaningful. Source-specific fields that will be used in the application are indicated as such. Side effects of this change: * Modifications to various parts of the view layer were necessary to retrofit this change. * In some cases, we are only mapping the necessary data. (E.g., TIMDEX links array only includes source link.) This is subject to change as we learn more about UX requirements. * The Primo result partial has been minimized to more closely resemble the TIMDEX partial. This is also subject to change based on UX requirements.
1 parent f229be7 commit cf4d71a

15 files changed

+567
-83
lines changed

app/controllers/search_controller.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,11 @@ def load_gdt_results
4747

4848
# Handle errors
4949
@errors = extract_errors(response)
50-
@pagination = Analyzer.new(@enhanced_query, response).pagination if @errors.nil?
51-
@results = extract_results(response)
50+
return unless @errors.nil?
51+
52+
@pagination = Analyzer.new(@enhanced_query, response).pagination
53+
raw_results = extract_results(response)
54+
@results = NormalizeTimdexResults.new(raw_results, @enhanced_query[:q]).normalize
5255
@filters = extract_filters(response)
5356
end
5457

@@ -77,8 +80,11 @@ def load_timdex_results
7780
response = query_timdex(query)
7881

7982
@errors = extract_errors(response)
80-
@pagination = Analyzer.new(@enhanced_query, response).pagination if @errors.nil?
81-
@results = extract_results(response)
83+
return unless @errors.nil?
84+
85+
@pagination = Analyzer.new(@enhanced_query, response).pagination
86+
raw_results = extract_results(response)
87+
@results = NormalizeTimdexResults.new(raw_results, @enhanced_query[:q]).normalize
8288
end
8389

8490
def active_filters

app/helpers/search_helper.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ def format_highlight_label(field_name)
1515
end
1616

1717
def view_online(result)
18-
return unless result['sourceLink'].present?
18+
return unless result['source_link'].present?
1919

20-
link_to 'View online', result['sourceLink'], class: 'button button-primary'
20+
link_to 'View online', result['source_link'], class: 'button button-primary'
2121
end
2222

2323
def view_record(record_id)

app/models/normalize_primo_record.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Transforms a PNX doc from Primo Search API into a normalized record.
1+
# Transforms a Primo Search API result into a normalized record.
22
class NormalizePrimoRecord
33
def initialize(record, query)
44
@record = record
@@ -7,22 +7,24 @@ def initialize(record, query)
77

88
def normalize
99
{
10+
# Core fields
1011
'title' => title,
1112
'creators' => creators,
1213
'source' => source,
1314
'year' => year,
1415
'format' => format,
1516
'links' => links,
1617
'citation' => citation,
17-
'container' => container_title,
1818
'identifier' => record_id,
1919
'summary' => summary,
20-
'numbering' => numbering,
21-
'chapter_numbering' => chapter_numbering,
22-
'thumbnail' => thumbnail,
2320
'publisher' => publisher,
2421
'location' => best_location,
2522
'subjects' => subjects,
23+
# Primo-specific fields
24+
'container' => container_title,
25+
'numbering' => numbering,
26+
'chapter_numbering' => chapter_numbering,
27+
'thumbnail' => thumbnail,
2628
'availability' => best_availability,
2729
'other_availability' => other_availability?
2830
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Transforms a TIMDEX result into a normalized record.
2+
class NormalizeTimdexRecord
3+
def initialize(record, query)
4+
@record = record
5+
@query = query
6+
end
7+
8+
def normalize
9+
{
10+
# Core fields
11+
'title' => title,
12+
'creators' => creators,
13+
'source' => source,
14+
'year' => year,
15+
'format' => format,
16+
'links' => links,
17+
'citation' => citation,
18+
'identifier' => record_id,
19+
'summary' => summary,
20+
'publisher' => publisher,
21+
'location' => location,
22+
'subjects' => subjects,
23+
# TIMDEX-specific fields
24+
'content_type' => content_type,
25+
'dates' => dates,
26+
'contributors' => contributors,
27+
'highlight' => highlight,
28+
'source_link' => source_link
29+
}
30+
end
31+
32+
private
33+
34+
def title
35+
@record['title'] || 'Unknown title'
36+
end
37+
38+
def creators
39+
return [] unless @record['contributors']
40+
41+
# Convert TIMDEX contributors to Primo-style creators format
42+
@record['contributors']
43+
.select { |c| %w[Creator Author].include?(c['kind']) }
44+
.map { |creator| { 'value' => creator['value'], 'link' => nil } }
45+
end
46+
47+
def source
48+
@record['source']&.first || 'Unknown source'
49+
end
50+
51+
def year
52+
# Extract year from dates
53+
return nil unless @record['dates']
54+
55+
pub_date = @record['dates'].find { |date| date['kind'] == 'Publication date' }
56+
return pub_date['value']&.match(/\d{4}/)&.to_s if pub_date
57+
58+
# Fallback to any date with a year
59+
@record['dates'].each do |date|
60+
year_match = date['value']&.match(/\d{4}/)
61+
return year_match.to_s if year_match
62+
end
63+
end
64+
65+
def format
66+
return '' unless @record['contentType']
67+
68+
@record['contentType'].map { |type| type['value'] }.join(' ; ')
69+
end
70+
71+
def links
72+
links = []
73+
74+
# Add source link if available
75+
if @record['sourceLink']
76+
links << {
77+
'kind' => 'full record',
78+
'url' => @record['sourceLink'],
79+
'text' => 'View full record'
80+
}
81+
end
82+
83+
links
84+
end
85+
86+
def citation
87+
@record['citation'] || nil
88+
end
89+
90+
def summary
91+
return nil unless @record['summary']
92+
93+
@record['summary'].is_a?(Array) ? @record['summary'].join(' ') : @record['summary']
94+
end
95+
96+
def publisher
97+
# Extract from contributors or other fields
98+
return nil unless @record['contributors']
99+
100+
publisher = @record['contributors'].find { |c| c['kind'] == 'Publisher' }
101+
publisher&.dig('value')
102+
end
103+
104+
def location
105+
return nil unless @record['locations']
106+
107+
@record['locations'].map { |loc| loc['value'] }.compact.join('; ')
108+
end
109+
110+
def subjects
111+
return [] unless @record['subjects']
112+
113+
@record['subjects'].map { |subject| subject['value'] }
114+
end
115+
116+
def record_id
117+
@record['timdexRecordId']
118+
end
119+
120+
# TIMDEX-specific methods
121+
def content_type
122+
@record['contentType']
123+
end
124+
125+
def dates
126+
@record['dates']
127+
end
128+
129+
def contributors
130+
@record['contributors']
131+
end
132+
133+
def highlight
134+
@record['highlight']
135+
end
136+
137+
def source_link
138+
@record['sourceLink']
139+
end
140+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Batch normalization for TIMDEX API results
2+
class NormalizeTimdexResults
3+
def initialize(results, query)
4+
@results = results
5+
@query = query
6+
end
7+
8+
def normalize
9+
return [] unless @results.is_a?(Array)
10+
11+
@results.filter_map do |doc|
12+
NormalizeTimdexRecord.new(doc, @query).normalize
13+
end
14+
end
15+
end

app/views/search/_result.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<li class="result">
22
<div class="result-content">
33
<h3 class="record-title">
4-
<span class="sr">Title: </span><%= link_to(result['title'], record_path(result['timdexRecordId'])) %>
4+
<span class="sr">Title: </span><%= link_to(result['title'], record_path(result['identifier'])) %>
55
</h3>
66

77
<p class="pub-info">
8-
<span><%= result['contentType']&.each { |type| type['value'] }&.join(' ; ') %></span>
8+
<span><%= result['content_type']&.each { |type| type['value'] }&.join(' ; ') %></span>
99
<span>
1010
<% result['dates']&.each do |date| %>
1111
<%= date['value'] if date['kind'] == 'Publication date' %>
Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,45 @@
11
<li class="result">
22
<div class="result-content">
33
<h3 class="record-title">
4-
<span class="sr">Title: </span><%= link_to(result_geo['title'], record_path(result_geo['timdexRecordId'])) %>
4+
<span class="sr">Title: </span><%= link_to(result_geo['title'], record_path(result_geo['timdex_record_id'])) %>
55
</h3>
66

7-
<div class="data-info">
8-
<%= render partial: 'shared/geo_data_info', locals: { metadata: result_geo } %>
9-
</div>
7+
<% if result_geo['content_type'] || result_geo['dates'] %>
8+
<div class="data-info">
9+
<%= render partial: 'shared/geo_data_info', locals: {
10+
metadata: {
11+
'contentType' => result_geo['content_type'],
12+
'dates' => result_geo['dates']
13+
}
14+
} %>
15+
</div>
16+
<% end %>
1017

11-
<% if result_geo['contributors'] %>
18+
<% if result_geo['creators'].present? || result_geo['contributors'].present? %>
1219
<span class="sr">Contributors: </span>
1320
<ul class="list-inline truncate-list contributors">
14-
<%= render partial: 'shared/contributors', locals: { contributors: result_geo['contributors'] } %>
21+
<!-- Use normalized creators if available, otherwise fall back to raw contributors -->
22+
<% contributors = result_geo['creators'].present? ? result_geo['creators'] : result_geo['contributors'] %>
23+
<% if contributors %>
24+
<%= render partial: 'shared/contributors', locals: { contributors: contributors } %>
25+
<% end %>
1526
</ul>
1627
<% end %>
1728

18-
<% if result_geo['summary'] %>
29+
<% if result_geo['summary'].present? %>
1930
<p class="result-summary truncate-list">
20-
<span class="sr">Summary: </span><%= result_geo['summary'].join(' ') %>
31+
<span class="sr">Summary: </span><%= result_geo['summary'] %>
2132
</p>
2233
<% end %>
2334

2435
<% if result_geo['highlight'] %>
2536
<div class="result-highlights">
26-
<%= render partial: 'search/highlights', locals: { result: result_geo } %>
37+
<%= render partial: 'search/highlights', locals: { result: { 'highlight' => result_geo['highlight'] } } %>
2738
</div>
2839
<% end %>
2940

3041
<div class="result-record">
31-
<%= view_record(result_geo['timdexRecordId']) %>
42+
<%= view_record(result_geo['timdex_record_id']) %>
3243
</div>
3344
</div>
3445
</li>

app/views/search/_result_primo.html.erb

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@
22
<div class="result-content">
33
<h3 class="record-title">
44
<span class="sr">Title: </span>
5-
<% if result_primo['links']&.find { |link| link['kind'] == 'full record' } %>
6-
<%= link_to(result_primo['title'], result_primo['links'].find { |link| link['kind'] == 'full record' }['url']) %>
5+
<% if result['links']&.find { |link| link['kind'] == 'full record' } %>
6+
<%= link_to(result['title'], result['links'].find { |link| link['kind'] == 'full record' }['url']) %>
77
<% else %>
8-
<%= result_primo['title'] %>
8+
<%= result['title'] %>
99
<% end %>
1010
</h3>
1111

1212
<p class="pub-info">
13-
<span><%= result_primo['format'] %></span>
14-
<span><%= result_primo['year'] %></span>
13+
<span><%= result['format'] %></span>
14+
<span><%= result['year'] %></span>
1515
</p>
1616

17-
<% if result_primo['creators'].present? %>
17+
<% if result['creators'].present? %>
1818
<span class="sr">Contributors: </span>
1919
<ul class="list-inline truncate-list contributors">
20-
<% result_primo['creators'].each do |creator| %>
20+
<% result['creators'].each do |creator| %>
2121
<li>
2222
<% if creator[:link] %>
2323
<%= link_to creator[:value], creator[:link] %>
@@ -29,49 +29,12 @@
2929
</ul>
3030
<% end %>
3131

32-
<% if result_primo['container'].present? %>
33-
<p class="pub-info">
34-
<span class="sr">Published in: </span>
35-
<em><%= result_primo['container'] %></em>
36-
</p>
37-
<% end %>
38-
39-
<% if result_primo['citation'].present? %>
40-
<p class="citation">
41-
<span class="sr">Citation: </span>
42-
<%= result_primo['citation'] %>
43-
</p>
44-
<% end %>
45-
46-
<% if result_primo['summary'].present? %>
47-
<p class="summary">
48-
<span class="sr">Summary: </span>
49-
<%= truncate(result_primo['summary'], length: 300) %>
50-
</p>
51-
<% end %>
52-
53-
<% if result_primo['subjects'].present? %>
54-
<p class="subjects">
55-
<span class="sr">Subjects: </span>
56-
<%= result_primo['subjects'].join('; ') %>
57-
</p>
58-
<% end %>
59-
60-
<% if result_primo['links'].present? %>
61-
<ul class="list-inline links">
62-
<% result_primo['links'].each do |link| %>
63-
<li>
64-
<%= link_to link['kind'].titleize, link['url'], class: 'link-button' %>
65-
</li>
32+
<div class="result-get">
33+
<% if result['links'].present? %>
34+
<% result['links'].each do |link| %>
35+
<%= link_to link['kind'].titleize, link['url'], class: 'link-button' %>
6636
<% end %>
67-
</ul>
68-
<% end %>
69-
70-
<% if result_primo['availability'].present? %>
71-
<p class="availability">
72-
<span class="sr">Availability: </span>
73-
<span class="availability-status"><%= result_primo['availability'] %></span>
74-
</p>
75-
<% end %>
37+
<% end %>
38+
</div>
7639
</div>
77-
</li>
40+
</li>

0 commit comments

Comments
 (0)