Skip to content

Commit

Permalink
Merge pull request #78 from LD4P/loc-lookup
Browse files Browse the repository at this point in the history
Extend LOC lookup support from qa gem
  • Loading branch information
chrisrlc authored Nov 1, 2023
2 parents db0bdd6 + 8eff316 commit 6b97f61
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 1 deletion.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
/.git
/.gitignore
/.rspec
/.rubocop*
/.qa_server_app

/coverage/*
Expand Down
20 changes: 20 additions & 0 deletions lib/qa/authorities/loc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Overrides loc authority from qa gem to handle rdftypes for subauthorities

require 'uri'

module Qa::Authorities
module Loc
extend AuthorityWithSubAuthority

require 'qa/authorities/loc/generic_authority'
def self.subauthority_for(subauthority)
validate_subauthority!(subauthority)
GenericAuthority.new(subauthority)
end

extend LocSubauthority
def self.subauthorities
authorities + vocabularies + datatypes + preservation + rdftypes
end
end
end
93 changes: 93 additions & 0 deletions lib/qa/authorities/loc/generic_authority.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Overrides loc authority from qa gem to customize search output and handle
# rdftypes for subauthorities

module Qa::Authorities
class Loc::GenericAuthority < Base
attr_reader :subauthority
def initialize(subauthority)
@subauthority = subauthority
end

include WebServiceBase

def response(url)
uri = URI(url)
conn = Faraday.new "#{uri.scheme}://#{uri.host}"
conn.options.params_encoder = Faraday::FlatParamsEncoder
conn.get do |req|
req.headers['Accept'] = 'application/json'
req.url uri.path
req.params = Rack::Utils.parse_query(uri.query)
end
end

def search(q)
@raw_response = json(build_query_url(q))
parse_authority_response
end

def build_query_url(q)
escaped_query = ERB::Util.url_encode(q)
authority = Loc.get_authority(subauthority)
rdftype = Loc.get_rdftype(subauthority)
authority_fragment = Loc.get_url_for_authority(authority) + ERB::Util.url_encode(authority)
rdftype_fragment = "&q=rdftype:#{ERB::Util.url_encode(rdftype)}" if rdftype
"https://id.loc.gov/search/?q=#{escaped_query}&q=#{authority_fragment}#{rdftype_fragment}&format=json"
end

def find(id)
json(find_url(id))
end

def find_url(id)
"https://id.loc.gov/authorities/#{@subauthority}/#{id}.json"
end

private

# Reformats the data received from the LOC service
# Filters for id uniqueness
def parse_authority_response
results = @raw_response.select { |response| response[0] == "atom:entry" }.map do |response|
loc_response_to_qa(response_to_struct(response))
end
results.uniq { |result| result["id"] }
end

# Converts most of the atom data into an OpenStruct object.
#
# Note that this is a pretty naive conversion. There should probably just
# be a class that properly translates and stores the various pieces of
# data, especially if this logic could be useful in other auth lookups.
def response_to_struct(response)
contents = response.each_with_object({}) do |result_parts, result|
next unless result_parts[0]
key = result_parts[0].sub('atom:', '').sub('dcterms:', '')
info = result_parts[1]
val = result_parts[2]

case key
when 'title', 'id', 'name', 'updated', 'created'
result[key] = val
when 'link'
result["links"] ||= []
result["links"] << { "type" => info["type"], "href" => info["href"] }
end
end

OpenStruct.new(contents)
end

# Simple conversion from LoC-based struct to QA hash
def loc_response_to_qa(data)
response = {
"id" => data.id || data.title,
"label" => data.title
}

response["uri"] = (data.links.find { |l| l["type"].nil? } || data.links[0])["href"] if data.links.present?

response
end
end
end
161 changes: 161 additions & 0 deletions lib/qa/authorities/loc_subauthority.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Overrides loc authority from qa gem to handle rdftypes for subauthorities

module Qa::Authorities::LocSubauthority # rubocop:disable Metrics/ModuleLength
def get_url_for_authority(authority)
if authorities.include?(authority) then authority_base_url
elsif vocabularies.include?(authority) then vocab_base_url
elsif datatypes.include?(authority) then datatype_base_url
elsif preservation.include?(authority) then vocab_preservation_base_url
end
end

def get_authority(authority)
rdftypes.include?(authority) ? authority.split('_')[0] : authority
end

def get_rdftype(authority)
rdftypes.include?(authority) ? authority.split('_')[-1] : nil
end

def authorities
[
"classification",
"childrensSubjects",
"demographicTerms",
"genreForms",
"names",
"performanceMediums",
"subjects"
]
end

def vocabularies # rubocop:disable Metrics/MethodLength
[
"actionsGranted",
"agentType",
"bookformat",
"carriers",
"classSchemes",
"contentTypes",
"countries",
"descriptionConventions",
"ethnographicTerms",
"frequencies",
"geographicAreas",
"graphicMaterials",
"iso639-1",
"iso639-2",
"iso639-5",
"issuance",
"languages",
"marcauthen",
"maspect",
"maudience",
"mbroadstd",
"mcapturestorage",
"mcolor",
"mediaTypes",
"mencformat",
"menclvl",
"mfiletype",
"mfont",
"mgeneration",
"mgovtpubtype",
"mgroove",
"millus",
"mlayout",
"mmaterial",
"mmusicformat",
"mmusnotation",
"mnotetype",
"mplayback",
"mplayspeed",
"mpolarity",
"mpresformat",
"mproduction",
"mprojection",
"mrecmedium",
"mrectype",
"mreductionratio",
"mregencoding",
"mrelief",
"mscale",
"mscript",
"mserialpubtype",
"msoundcontent",
"mspecplayback",
"mstatus",
"msupplcont",
"mtactile",
"mtapeconfig",
"mtechnique",
"mvidformat",
"organizations",
"preservation",
"rbmscv",
"rbmsrel",
"relationship",
"relators",
"resourceComponents"
]
end

def datatypes
["edtf"]
end

def preservation # rubocop:disable Metrics/MethodLength
[
"contentLocationType",
"copyrightStatus",
"cryptographicHashFunctions",
"environmentCharacteristic",
"environmentPurpose",
"eventRelatedAgentRole",
"eventRelatedObjectRole",
"eventType",
"formatRegistryRole",
"hardwareType",
"inhibitorTarget",
"inhibitorType",
"objectCategory",
"preservationLevelRole",
"relationshipSubType",
"relationshipType",
"rightsBasis",
"rightsRelatedAgentRole",
"signatureEncoding",
"signatureMethod",
"softwareType",
"storageMedium"
]
end

def rdftypes
[
"names_ConferenceName",
"names_CorporateName",
"names_FamilyName",
"names_Geographic",
"names_PersonalName"
]
end

private

def vocab_base_url
"cs%3Ahttp%3A%2F%2Fid.loc.gov%2Fvocabulary%2F"
end

def authority_base_url
"cs%3Ahttp%3A%2F%2Fid.loc.gov%2Fauthorities%2F"
end

def datatype_base_url
"cs%3Ahttp%3A%2F%2Fid.loc.gov%2Fdatatypes%2F"
end

def vocab_preservation_base_url
"cs%3Ahttp%3A%2F%2Fid.loc.gov%2Fvocabulary%2Fpreservation%2F"
end
end

0 comments on commit 6b97f61

Please sign in to comment.