qa 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/controllers/qa/linked_data_terms_controller.rb +30 -9
- data/app/controllers/qa/terms_controller.rb +3 -2
- data/app/models/qa/linked_data/config/context_property_map.rb +6 -25
- data/app/services/qa/iri_template_service.rb +32 -24
- data/app/services/qa/linked_data/authority_service.rb +8 -0
- data/app/services/qa/linked_data/authority_url_service.rb +27 -8
- data/app/services/qa/linked_data/deep_sort_service.rb +3 -2
- data/app/services/qa/linked_data/graph_service.rb +13 -0
- data/app/services/qa/linked_data/language_service.rb +12 -0
- data/app/services/qa/linked_data/language_sort_service.rb +7 -2
- data/app/services/qa/linked_data/ldpath_service.rb +40 -0
- data/app/services/qa/linked_data/mapper/graph_ldpath_mapper_service.rb +49 -0
- data/app/services/qa/linked_data/mapper/graph_mapper_service.rb +3 -11
- data/app/services/qa/linked_data/mapper/graph_predicate_mapper_service.rb +40 -0
- data/app/services/qa/linked_data/mapper/search_results_mapper_service.rb +58 -11
- data/app/services/qa/linked_data/mapper/term_results_mapper_service.rb +80 -0
- data/config/authorities/linked_data/loc.json +13 -7
- data/config/authorities/linked_data/oclc_fast.json +13 -8
- data/lib/generators/qa/discogs/USAGE +10 -0
- data/lib/generators/qa/discogs/discogs_generator.rb +12 -0
- data/lib/generators/qa/discogs/templates/config/discogs-formats.yml +346 -0
- data/lib/generators/qa/discogs/templates/config/discogs-genres.yml +627 -0
- data/lib/generators/qa/install/templates/config/initializers/qa.rb +4 -0
- data/lib/qa.rb +6 -0
- data/lib/qa/authorities.rb +2 -0
- data/lib/qa/authorities/discogs.rb +28 -0
- data/lib/qa/authorities/discogs/discogs_instance_builder.rb +145 -0
- data/lib/qa/authorities/discogs/discogs_translation.rb +126 -0
- data/lib/qa/authorities/discogs/discogs_utils.rb +89 -0
- data/lib/qa/authorities/discogs/discogs_works_builder.rb +153 -0
- data/lib/qa/authorities/discogs/generic_authority.rb +151 -0
- data/lib/qa/authorities/discogs_subauthority.rb +9 -0
- data/lib/qa/authorities/linked_data/config.rb +7 -3
- data/lib/qa/authorities/linked_data/config/search_config.rb +99 -11
- data/lib/qa/authorities/linked_data/config/term_config.rb +112 -8
- data/lib/qa/authorities/linked_data/find_term.rb +154 -84
- data/lib/qa/authorities/linked_data/search_query.rb +76 -13
- data/lib/qa/configuration.rb +8 -0
- data/lib/qa/version.rb +1 -1
- data/spec/controllers/linked_data_terms_controller_spec.rb +151 -30
- data/spec/controllers/terms_controller_spec.rb +4 -0
- data/spec/features/linked_data/language_spec.rb +298 -0
- data/spec/fixtures/authorities/linked_data/lod_full_config.json +21 -5
- data/spec/fixtures/authorities/linked_data/lod_lang_defaults.json +4 -4
- data/spec/fixtures/authorities/linked_data/lod_lang_multi_defaults.json +4 -4
- data/spec/fixtures/authorities/linked_data/lod_lang_no_defaults.json +4 -5
- data/spec/fixtures/authorities/linked_data/lod_lang_param.json +4 -4
- data/spec/fixtures/authorities/linked_data/lod_term_uri_param_config.json +1 -1
- data/spec/fixtures/discogs-find-response-json.json +1 -0
- data/spec/fixtures/discogs-find-response-jsonld-master.json +1 -0
- data/spec/fixtures/discogs-find-response-jsonld-release.json +1 -0
- data/spec/fixtures/discogs-id-matches-master.json +1 -0
- data/spec/fixtures/discogs-id-matches-release.json +1 -0
- data/spec/fixtures/discogs-id-not-found-master.json +1 -0
- data/spec/fixtures/discogs-id-not-found-release.json +1 -0
- data/spec/fixtures/discogs-search-response-no-auth.json +1 -0
- data/spec/fixtures/discogs-search-response-no-subauth.json +1 -0
- data/spec/fixtures/discogs-search-response-subauth.json +1 -0
- data/spec/fixtures/lod_lang_search_enesfrde.rdf.xml +60 -0
- data/spec/fixtures/lod_lang_search_sv.rdf.xml +42 -0
- data/spec/fixtures/lod_loc_term_found.rdf.xml +5 -0
- data/spec/lib/authorities/discogs/generic_authority_spec.rb +235 -0
- data/spec/lib/authorities/discogs_spec.rb +17 -0
- data/spec/lib/authorities/linked_data/config_spec.rb +68 -5
- data/spec/lib/authorities/linked_data/find_term_spec.rb +298 -3
- data/spec/lib/authorities/linked_data/generic_authority_spec.rb +46 -485
- data/spec/lib/authorities/linked_data/search_config_spec.rb +154 -3
- data/spec/lib/authorities/linked_data/search_query_spec.rb +240 -3
- data/spec/lib/authorities/linked_data/term_config_spec.rb +193 -5
- data/spec/lib/configuration_spec.rb +18 -0
- data/spec/models/linked_data/config/context_property_map_spec.rb +3 -31
- data/spec/services/iri_template_service_spec.rb +54 -12
- data/spec/{lib/authorities → services}/linked_data/authority_service_spec.rb +47 -0
- data/spec/services/linked_data/language_service_spec.rb +52 -11
- data/spec/services/linked_data/ldpath_service_spec.rb +61 -0
- data/spec/services/linked_data/mapper/graph_ldpath_mapper_service_spec.rb +118 -0
- data/spec/services/linked_data/mapper/graph_predicate_mapper_service_spec.rb +110 -0
- data/spec/services/linked_data/mapper/term_results_mapper_service_spec.rb +94 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/matchers/include_hash.rb +5 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +4 -0
- metadata +73 -5
- data/lib/qa/authorities/linked_data/rdf_helper.rb +0 -49
|
@@ -14,4 +14,8 @@ Qa.config do |config|
|
|
|
14
14
|
# For linked data access, specify default language for sorting and selection. The default is only used if a language is not
|
|
15
15
|
# specified in the authority's configuration file and not passed in as a parameter. (e.g. :en, [:en], or [:en, :fr])
|
|
16
16
|
# config.default_language = :en
|
|
17
|
+
|
|
18
|
+
# When true, prevents ldpath requests from making additional network calls. All values will come from the context graph
|
|
19
|
+
# passed to the ldpath request.
|
|
20
|
+
# config.limit_ldpath_to_context = true
|
|
17
21
|
end
|
data/lib/qa.rb
CHANGED
|
@@ -24,6 +24,12 @@ module Qa
|
|
|
24
24
|
@config
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
def self.deprecation_warning(in_msg: nil, msg:)
|
|
28
|
+
return if Rails.env == 'test'
|
|
29
|
+
in_msg = in_msg.present? ? "In #{in_msg}, " : ''
|
|
30
|
+
warn "[DEPRECATED] #{in_msg}#{msg} It will be removed in the next major release."
|
|
31
|
+
end
|
|
32
|
+
|
|
27
33
|
# Raised when the configuration directory for local authorities doesn't exist
|
|
28
34
|
class ConfigDirectoryNotFound < StandardError; end
|
|
29
35
|
|
data/lib/qa/authorities.rb
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Qa::Authorities
|
|
2
|
+
# Provide authority namespace
|
|
3
|
+
module Discogs
|
|
4
|
+
extend ActiveSupport::Autoload
|
|
5
|
+
autoload :GenericAuthority
|
|
6
|
+
autoload :DiscogsTranslation
|
|
7
|
+
autoload :DiscogsUtils
|
|
8
|
+
autoload :DiscogsWorksBuilder
|
|
9
|
+
autoload :DiscogsInstanceBuilder
|
|
10
|
+
|
|
11
|
+
extend AuthorityWithSubAuthority
|
|
12
|
+
extend DiscogsSubauthority
|
|
13
|
+
|
|
14
|
+
require 'qa/authorities/discogs/generic_authority'
|
|
15
|
+
# Create an authority object for given subauthority
|
|
16
|
+
#
|
|
17
|
+
# @param [String] subauthority to use
|
|
18
|
+
# @return [GenericAuthority]
|
|
19
|
+
def self.subauthority_for(subauthority)
|
|
20
|
+
validate_subauthority!(subauthority)
|
|
21
|
+
GenericAuthority.new(subauthority)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.subauthorities
|
|
25
|
+
authorities
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require 'rdf'
|
|
2
|
+
module Qa::Authorities
|
|
3
|
+
module Discogs
|
|
4
|
+
module DiscogsInstanceBuilder
|
|
5
|
+
include Discogs::DiscogsUtils
|
|
6
|
+
|
|
7
|
+
# @param [Hash] the http response from discogs
|
|
8
|
+
# @return [Array] rdf statements
|
|
9
|
+
def get_format_stmts(response)
|
|
10
|
+
stmts = []
|
|
11
|
+
# The Discogs formats array contains several types of information: the "name" field defines the type of
|
|
12
|
+
# audio disc (e.g., CD vs vinyl), and the "descriptions" field can contain the playing speed, playback
|
|
13
|
+
# and release information. Process the "name" field first, then the "descriptions"
|
|
14
|
+
# In unusual cases, there can be multiple playing speeds. Need to distinguish among them.
|
|
15
|
+
count = 1
|
|
16
|
+
return stmts unless response["formats"].present?
|
|
17
|
+
response["formats"].each do |format|
|
|
18
|
+
stmts.concat(build_format_name_stmts(format["name"]))
|
|
19
|
+
# Now process playing speed, playback channel and release info
|
|
20
|
+
stmts.concat(build_format_desc_stmts(format["descriptions"], count))
|
|
21
|
+
count += 1
|
|
22
|
+
end
|
|
23
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param [Hash] the http response from discogs
|
|
27
|
+
# @return [Array] rdf statements
|
|
28
|
+
def get_labels_stmts(response)
|
|
29
|
+
stmts = []
|
|
30
|
+
# Various roles are defined as provision activities, such as the record company (or label),
|
|
31
|
+
# the publishing house, the recording studio, etc. These are defined separately in the Discogs
|
|
32
|
+
# data, so combine them into a single array to be processed in one iteration.
|
|
33
|
+
provision_activity_array = response["labels"] if response["labels"].present?
|
|
34
|
+
provision_activity_array += response["companies"] if response["companies"].present?
|
|
35
|
+
stmts += build_provision_activity_stmts(provision_activity_array) if provision_activity_array.present?
|
|
36
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param [Hash] the http response from discogs
|
|
40
|
+
# @return [Array] rdf statements
|
|
41
|
+
def get_identifiers_stmts(response)
|
|
42
|
+
stmts = []
|
|
43
|
+
# The Discogs data includes identifiers such as side label codes and rights society codes.
|
|
44
|
+
count = 1
|
|
45
|
+
return stmts unless response["identifiers"].present?
|
|
46
|
+
response["identifiers"].each do |activity|
|
|
47
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/identifiedBy", "Identifier#{count}")
|
|
48
|
+
stmts << contruct_stmt_uri_object("Identifier#{count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Identifier")
|
|
49
|
+
stmts << contruct_stmt_literal_object("Identifier#{count}", rdfs_label_predicate, activity["value"])
|
|
50
|
+
count += 1
|
|
51
|
+
end
|
|
52
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @param [Array] format descriptions discogs
|
|
56
|
+
# @param [Integer] incremented on the call from get_format_stmts
|
|
57
|
+
# @return [Array] rdf statements
|
|
58
|
+
def build_format_desc_stmts(descs, count)
|
|
59
|
+
stmts = []
|
|
60
|
+
return stmts unless descs.present?
|
|
61
|
+
descs.each do |desc|
|
|
62
|
+
# map discogs description field to the corresponding LOC type
|
|
63
|
+
df = discogs_formats[desc.gsub(/\s+/, "")]
|
|
64
|
+
if df.present?
|
|
65
|
+
stmts += build_format_characteristics(df, count)
|
|
66
|
+
else
|
|
67
|
+
stmts << contruct_stmt_literal_object("Instance1", "http://id.loc.gov/ontologies/bibframe/editionStatement", desc)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
stmts
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @param [Hash] discogs format descriptions "translated" to BIBFRAME terms
|
|
74
|
+
# @param [Integer] incremented on the call from build_format_desc_stmts
|
|
75
|
+
# @return [Array] rdf statements
|
|
76
|
+
def build_format_characteristics(df, count)
|
|
77
|
+
stmts = []
|
|
78
|
+
case df["type"]
|
|
79
|
+
when "playbackChannel"
|
|
80
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/soundCharacteristic", df["uri"])
|
|
81
|
+
stmts << contruct_stmt_uri_object(df["uri"], rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/PlaybackChannel")
|
|
82
|
+
stmts << contruct_stmt_literal_object(df["uri"], rdfs_label_predicate, df["label"])
|
|
83
|
+
when "dimension"
|
|
84
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/dimensions", df["label"])
|
|
85
|
+
when "playingSpeed"
|
|
86
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/soundCharacteristic", "PlayingSpeed#{count}")
|
|
87
|
+
stmts << contruct_stmt_uri_object("PlayingSpeed#{count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/PlayingSpeed")
|
|
88
|
+
stmts << contruct_stmt_literal_object("PlayingSpeed#{count}", rdfs_label_predicate, df["label"])
|
|
89
|
+
end
|
|
90
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# map discogs name field to the corresponding LOC carrier
|
|
94
|
+
# @param [String] format name
|
|
95
|
+
# @return [Array] rdf statements
|
|
96
|
+
def build_format_name_stmts(name)
|
|
97
|
+
stmts = []
|
|
98
|
+
return stmts unless name.present?
|
|
99
|
+
dc = discogs_formats[name.gsub(/\s+/, "")]
|
|
100
|
+
if dc.present?
|
|
101
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/carrier", dc["uri"])
|
|
102
|
+
stmts << contruct_stmt_uri_object(dc["uri"], rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Carrier")
|
|
103
|
+
stmts << contruct_stmt_literal_object(dc["uri"], rdfs_label_predicate, dc["label"])
|
|
104
|
+
stmts.concat(build_base_materials(name))
|
|
105
|
+
else
|
|
106
|
+
# if it's not a carrier, it's an edition statement
|
|
107
|
+
stmts << contruct_stmt_literal_object("Instance1", "http://id.loc.gov/ontologies/bibframe/editionStatement", name)
|
|
108
|
+
end
|
|
109
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @param [String] format name
|
|
113
|
+
# @return [Array] rdf statements
|
|
114
|
+
def build_base_materials(name)
|
|
115
|
+
stmts = []
|
|
116
|
+
return stmts unless name == "Vinyl" || name == "Shellac"
|
|
117
|
+
id = name == "Vinyl" ? "300014502" : "300014918"
|
|
118
|
+
|
|
119
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/baseMaterial", "http://vocab.getty.edu/aat/" + id)
|
|
120
|
+
stmts << contruct_stmt_uri_object("http://vocab.getty.edu/aat/" + id, rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/BaseMaterial")
|
|
121
|
+
stmts << contruct_stmt_literal_object("http://vocab.getty.edu/aat/" + id, rdfs_label_predicate, name)
|
|
122
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @param [Array] discogs roles "translated" into BIBFRAME provisional activities
|
|
126
|
+
# @return [Array] rdf statements
|
|
127
|
+
def build_provision_activity_stmts(activities)
|
|
128
|
+
stmts = []
|
|
129
|
+
# need to distinguish among different provision activities and roles
|
|
130
|
+
count = 1
|
|
131
|
+
activities.each do |activity|
|
|
132
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/provisionActivity", "ProvisionActivity#{count}")
|
|
133
|
+
stmts << contruct_stmt_uri_object("ProvisionActivity#{count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/ProvisionActivity")
|
|
134
|
+
stmts << contruct_stmt_uri_object("ProvisionActivity#{count}", bf_agent_predicate, activity["name"])
|
|
135
|
+
stmts << contruct_stmt_uri_object(activity["name"], rdf_type_predicate, bf_agent_type_object)
|
|
136
|
+
stmts << contruct_stmt_uri_object(activity["name"], bf_role_predicate, "PA_Role#{count}")
|
|
137
|
+
stmts << contruct_stmt_uri_object("PA_Role#{count}", rdf_type_predicate, bf_role_type_object)
|
|
138
|
+
stmts << contruct_stmt_literal_object("PA_Role#{count}", rdfs_label_predicate, activity["entity_type_name"])
|
|
139
|
+
count += 1
|
|
140
|
+
end
|
|
141
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
require 'rdf'
|
|
2
|
+
module Qa::Authorities
|
|
3
|
+
module Discogs
|
|
4
|
+
module DiscogsTranslation
|
|
5
|
+
include Discogs::DiscogsUtils
|
|
6
|
+
include Discogs::DiscogsWorksBuilder
|
|
7
|
+
include Discogs::DiscogsInstanceBuilder
|
|
8
|
+
|
|
9
|
+
# Returns modified Discogs data in json-ld format. The data is first structured as RDF
|
|
10
|
+
# statements that use the BIBFRAME ontology. Where applicable, Discogs terms are mapped
|
|
11
|
+
# to the URIs of corresponding objects in the Library of Congress vocabulary.
|
|
12
|
+
# @param [Hash] the http response from discogs
|
|
13
|
+
# @param [String] the subauthority
|
|
14
|
+
# @return [Array] jsonld
|
|
15
|
+
def build_graph(response, subauthority = "")
|
|
16
|
+
graph = RDF::Graph.new
|
|
17
|
+
|
|
18
|
+
rdf_statements = compile_rdf_statements(response, subauthority)
|
|
19
|
+
graph.insert_statements(rdf_statements)
|
|
20
|
+
|
|
21
|
+
graph.dump(:jsonld, standard_prefixes: true)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @param [Hash] the http response from discogs
|
|
25
|
+
# @param [String] the subauthority
|
|
26
|
+
# @return [Array] rdf statements
|
|
27
|
+
def compile_rdf_statements(response, subauthority)
|
|
28
|
+
complete_rdf_stmts = []
|
|
29
|
+
# The necessary statements depend on the subauthority. If the subauthority is master,
|
|
30
|
+
# all we need is a work and not an instance. If there's no subauthority, we can determine
|
|
31
|
+
# if the discogs record is a master because it will have a main_release field.
|
|
32
|
+
if master_only(response, subauthority)
|
|
33
|
+
complete_rdf_stmts.concat(build_master_statements(response))
|
|
34
|
+
else
|
|
35
|
+
# If the subauthority is not "master," we need to define an instance as well as a
|
|
36
|
+
# work. If the discogs record has a master_id, fetch that and use the results to
|
|
37
|
+
# build the statements for the work.
|
|
38
|
+
master_resp = response["master_id"].present? ? json("https://api.discogs.com/masters/#{response['master_id']}") : response
|
|
39
|
+
complete_rdf_stmts.concat(build_master_statements(master_resp))
|
|
40
|
+
# Now do the statements for the instance.
|
|
41
|
+
complete_rdf_stmts.concat(build_instance_statements(response))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @param [Hash] the http response from discogs
|
|
46
|
+
# @param [String] the subauthority
|
|
47
|
+
# @return [Boolean] returns true if the subauthority is "master" or the response contains a master
|
|
48
|
+
def master_only(response, subauthority)
|
|
49
|
+
return true if subauthority == "master"
|
|
50
|
+
return true if response["main_release"].present?
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param [Hash] the http response from discogs
|
|
55
|
+
# @return [Array] rdf statements
|
|
56
|
+
def build_master_statements(response)
|
|
57
|
+
# get the statements that define the primary work
|
|
58
|
+
master_stmts = get_primary_work_definition(response)
|
|
59
|
+
master_stmts.concat(get_primary_artists_stmts(response))
|
|
60
|
+
master_stmts.concat(get_extra_artists_stmts(response))
|
|
61
|
+
master_stmts.concat(get_genres_stmts(response))
|
|
62
|
+
# get the statements that define the secondary works by converting the tracklist
|
|
63
|
+
master_stmts.concat(get_tracklist_artists_stmts(response))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @param [Hash] the http response from discogs
|
|
67
|
+
# @return [Array] rdf statements
|
|
68
|
+
def build_instance_statements(response)
|
|
69
|
+
# get the statements that define the instance
|
|
70
|
+
instance_stmts = get_primary_instance_definition(response)
|
|
71
|
+
instance_stmts.concat(get_format_stmts(response))
|
|
72
|
+
instance_stmts.concat(get_labels_stmts(response))
|
|
73
|
+
instance_stmts.concat(get_identifiers_stmts(response))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @param [Hash] the http response from discogs
|
|
77
|
+
# @return [Array] rdf statements
|
|
78
|
+
def get_primary_work_definition(response)
|
|
79
|
+
stmts = []
|
|
80
|
+
stmts << contruct_stmt_uri_object("Work1", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Work")
|
|
81
|
+
stmts << contruct_stmt_uri_object("Work1", "http://id.loc.gov/ontologies/bibframe/title", "Work1Title")
|
|
82
|
+
stmts << contruct_stmt_literal_object("Work1Title", bf_main_title_predicate, response["title"])
|
|
83
|
+
stmts << contruct_stmt_uri_object("Work1", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Audio")
|
|
84
|
+
stmts.concat(build_year_statements(response, "Work"))
|
|
85
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @param [Hash] the http response from discogs
|
|
89
|
+
# @return [Array] rdf statements
|
|
90
|
+
def get_primary_instance_definition(response)
|
|
91
|
+
stmts = []
|
|
92
|
+
stmts << contruct_stmt_uri_object("Work1", "http://id.loc.gov/ontologies/bibframe/hasInstance", "Instance1")
|
|
93
|
+
stmts << contruct_stmt_uri_object("Instance1", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Instance")
|
|
94
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/title", "Instance1Title")
|
|
95
|
+
stmts << contruct_stmt_literal_object("Instance1Title", bf_main_title_predicate, response["title"])
|
|
96
|
+
stmts << contruct_stmt_uri_object("Instance1", "http://id.loc.gov/ontologies/bibframe/identifiedBy", "IdentifierPrimary")
|
|
97
|
+
stmts << contruct_stmt_uri_object("IdentifierPrimary", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Identifier")
|
|
98
|
+
stmts << contruct_stmt_literal_object("IdentifierPrimary", "http://www.w3.org/1999/02/22-rdf-syntax-ns#value", response["id"])
|
|
99
|
+
stmts.concat(build_year_statements(response, "Instance"))
|
|
100
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @param [Hash] the http response from discogs
|
|
104
|
+
# @return [Array] rdf statements
|
|
105
|
+
def get_primary_artists_stmts(response)
|
|
106
|
+
stmts = []
|
|
107
|
+
# can have multiple artists as primary contributors to the work; need to distinguish among them
|
|
108
|
+
count = 1
|
|
109
|
+
# for secondary contributors to the work
|
|
110
|
+
if response["artists"].present?
|
|
111
|
+
response["artists"].each do |artist|
|
|
112
|
+
# we need the primary artists later when we loop through the track list, so build this array
|
|
113
|
+
primary_artists << artist
|
|
114
|
+
|
|
115
|
+
stmts << contruct_stmt_uri_object("Work1", "http://id.loc.gov/ontologies/bibframe/contribution", "Work1PrimaryContribution#{count}")
|
|
116
|
+
stmts << contruct_stmt_uri_object("Work1PrimaryContribution#{count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bflc/PrimaryContribution")
|
|
117
|
+
stmts << contruct_stmt_uri_object("Work1PrimaryContribution#{count}", bf_agent_predicate, artist["name"])
|
|
118
|
+
stmts << contruct_stmt_uri_object(artist["name"], rdf_type_predicate, bf_agent_type_object)
|
|
119
|
+
count += 1
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
stmts
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'rdf'
|
|
2
|
+
module Qa::Authorities
|
|
3
|
+
module Discogs
|
|
4
|
+
module DiscogsUtils
|
|
5
|
+
DISCOGS_GENRE_MAPPING = YAML.load_file(Rails.root.join("config", "discogs-genres.yml"))
|
|
6
|
+
DISCOGS_FORMATS_MAPPING = YAML.load_file(Rails.root.join("config", "discogs-formats.yml"))
|
|
7
|
+
|
|
8
|
+
# Constructs an RDF statement where the subject, predicate and object are all URIs
|
|
9
|
+
# @param [String] either a string used to create a unique URI or an LOC uri in string format
|
|
10
|
+
# @param [String] or [Class] either a BIBFRAME property uri in string format or an RDF::URI
|
|
11
|
+
# @param [String] or [Class] strings can be a label or BIBFRAME class uri; class is always RDF::URI
|
|
12
|
+
# @return [Class] RDF::Statement with uri as the object
|
|
13
|
+
def contruct_stmt_uri_object(subject, predicate, object)
|
|
14
|
+
RDF::Statement(RDF::URI.new(subject), RDF::URI(predicate), RDF::URI.new(object))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Constructs an RDF statement where the subject and predicate are URIs and the object is a literal
|
|
18
|
+
# @param [String] either a string used to create a unique URI or an LOC uri in string format
|
|
19
|
+
# @param [String] or [Class] either a BIBFRAME property uri in string format or an RDF::URI
|
|
20
|
+
# @param [String] or [Class] strings can be a label or BIBFRAME class uri; class is always RDF::URI
|
|
21
|
+
# @return [Class] RDF::Statement with a literal as the object
|
|
22
|
+
def contruct_stmt_literal_object(subject, predicate, object)
|
|
23
|
+
RDF::Statement(RDF::URI.new(subject), RDF::URI(predicate), RDF::Literal.new(object))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# frequently used predicates and objects
|
|
27
|
+
def rdf_type_predicate
|
|
28
|
+
RDF::URI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def rdfs_label_predicate
|
|
32
|
+
RDF::URI("http://www.w3.org/2000/01/rdf-schema#label")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def bf_main_title_predicate
|
|
36
|
+
RDF::URI("http://id.loc.gov/ontologies/bibframe/mainTitle")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def bf_agent_predicate
|
|
40
|
+
RDF::URI("http://id.loc.gov/ontologies/bibframe/agent")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def bf_agent_type_object
|
|
44
|
+
RDF::URI("http://id.loc.gov/ontologies/bibframe/Agent")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def bf_role_predicate
|
|
48
|
+
RDF::URI("http://id.loc.gov/ontologies/bibframe/role")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def bf_role_type_object
|
|
52
|
+
RDF::URI("http://id.loc.gov/ontologies/bibframe/Role")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def discogs_genres
|
|
56
|
+
DISCOGS_GENRE_MAPPING
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def discogs_formats
|
|
60
|
+
DISCOGS_FORMATS_MAPPING
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# both the work and the instance require a statement for the release year
|
|
64
|
+
# @param [Hash] the http response from discogs
|
|
65
|
+
# @param [String] either "Work" or "Instance"
|
|
66
|
+
# @return [Array] rdf statements
|
|
67
|
+
def build_year_statements(response, type)
|
|
68
|
+
year_stmts = []
|
|
69
|
+
if type == "Work" && response["year"].present?
|
|
70
|
+
year_stmts = get_year_rdf(type + "1", response["year"])
|
|
71
|
+
elsif response["released"].present?
|
|
72
|
+
year_stmts = get_year_rdf(type + "1", response["released"])
|
|
73
|
+
end
|
|
74
|
+
year_stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @param [String] either "Work1" or "Instance1"
|
|
78
|
+
# @param [String] 4-digit year in string format
|
|
79
|
+
# @return [Array] rdf statements
|
|
80
|
+
def get_year_rdf(type, year)
|
|
81
|
+
year_stmts = []
|
|
82
|
+
year_stmts << contruct_stmt_uri_object(type, "http://id.loc.gov/ontologies/bibframe/provisionActivity", "#{type}ProvisionActivityDate")
|
|
83
|
+
year_stmts << contruct_stmt_uri_object("#{type}ProvisionActivityDate", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/ProvisionActivity")
|
|
84
|
+
# Full RDF statement syntax as this one requires a datatype
|
|
85
|
+
year_stmts << RDF::Statement(RDF::URI.new("#{type}ProvisionActivityDate"), RDF::URI("http://id.loc.gov/ontologies/bibframe/date"), RDF::Literal.new(year.to_s, datatype: RDF::XSD.date))
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require 'rdf'
|
|
2
|
+
module Qa::Authorities
|
|
3
|
+
module Discogs
|
|
4
|
+
module DiscogsWorksBuilder # rubocop:disable Metrics/ModuleLength
|
|
5
|
+
include Discogs::DiscogsUtils
|
|
6
|
+
|
|
7
|
+
# @param [Hash] the http response from discogs
|
|
8
|
+
# @return [Array] rdf statements
|
|
9
|
+
def get_extra_artists_stmts(response)
|
|
10
|
+
stmts = []
|
|
11
|
+
# can have multiple artists as primary contributors to the work; need to distinguish among them
|
|
12
|
+
count = 1
|
|
13
|
+
return stmts unless response["extraartists"].present?
|
|
14
|
+
response["extraartists"].each do |artist|
|
|
15
|
+
stmts << contruct_stmt_uri_object("Work1", "http://id.loc.gov/ontologies/bibframe/contribution", "Work1SecondaryContribution#{count}")
|
|
16
|
+
stmts << contruct_stmt_uri_object("Work1SecondaryContribution#{count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Contribution")
|
|
17
|
+
stmts << contruct_stmt_uri_object("Work1SecondaryContribution#{count}", bf_agent_predicate, artist["name"])
|
|
18
|
+
stmts << contruct_stmt_uri_object(artist["name"], rdf_type_predicate, bf_agent_type_object)
|
|
19
|
+
stmts += build_artist_role_stmts(artist, count)
|
|
20
|
+
count += 1
|
|
21
|
+
end
|
|
22
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param [Hash] the extraartists defined at the release level, not the track level
|
|
26
|
+
# @param [Integer] gives the role a unique uri
|
|
27
|
+
# @return [Array] rdf statements
|
|
28
|
+
def build_artist_role_stmts(artist, count)
|
|
29
|
+
stmts = []
|
|
30
|
+
stmts << contruct_stmt_uri_object(artist["name"], bf_role_predicate, "Work1SecondaryContributor_Role#{count}")
|
|
31
|
+
stmts << contruct_stmt_uri_object("Work1SecondaryContributor_Role#{count}", rdf_type_predicate, bf_role_type_object)
|
|
32
|
+
stmts << contruct_stmt_literal_object("Work1SecondaryContributor_Role#{count}", rdfs_label_predicate, artist["role"])
|
|
33
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param [Hash] the http response from discogs
|
|
37
|
+
# @return [Array] rdf statements
|
|
38
|
+
def get_genres_stmts(response)
|
|
39
|
+
stmts = []
|
|
40
|
+
all_genres = []
|
|
41
|
+
all_genres += response["genres"] if response["genres"].present?
|
|
42
|
+
all_genres += response["styles"] if response["styles"].present?
|
|
43
|
+
return stmts unless all_genres.any?
|
|
44
|
+
all_genres.each do |genre|
|
|
45
|
+
stmts.concat(build_genres(genre))
|
|
46
|
+
end
|
|
47
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# map discogs genre to LOC genreForm
|
|
51
|
+
# @param [String] the name of a discogs style or genre
|
|
52
|
+
# @return [Array] rdf statements
|
|
53
|
+
def build_genres(genre)
|
|
54
|
+
stmts = []
|
|
55
|
+
dg = discogs_genres[genre.gsub(/\s+/, "")]
|
|
56
|
+
if dg.present?
|
|
57
|
+
stmts << contruct_stmt_uri_object("Work1", "http://id.loc.gov/ontologies/bibframe/genreForm", dg["uri"])
|
|
58
|
+
stmts << contruct_stmt_uri_object(dg["uri"], rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/GenreForm")
|
|
59
|
+
stmts << contruct_stmt_literal_object(dg["uri"], rdfs_label_predicate, dg["label"])
|
|
60
|
+
end
|
|
61
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param [String] the uri of the genreForm
|
|
65
|
+
# @param [String] the genreForm label
|
|
66
|
+
# @return [Array] rdf statements
|
|
67
|
+
def build_genres_and_styles(uri, dg_label)
|
|
68
|
+
stmts = []
|
|
69
|
+
stmts << contruct_stmt_uri_object("Work1", "http://id.loc.gov/ontologies/bibframe/genreForm", uri)
|
|
70
|
+
stmts << contruct_stmt_uri_object(uri, rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/GenreForm")
|
|
71
|
+
stmts << contruct_stmt_literal_object(uri, rdfs_label_predicate, dg_label)
|
|
72
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @param [Hash] the http response from discogs
|
|
76
|
+
# @return [Array] rdf statements
|
|
77
|
+
def get_tracklist_artists_stmts(response)
|
|
78
|
+
stmts = []
|
|
79
|
+
# individual tracks become secondary works; need to distinguish among them
|
|
80
|
+
w_count = 2
|
|
81
|
+
return stmts unless response["tracklist"].present?
|
|
82
|
+
response["tracklist"].each do |track|
|
|
83
|
+
stmts.concat(build_secondary_works(track, w_count))
|
|
84
|
+
# If the Discogs data includes the primary artists for each track, use those. If not,
|
|
85
|
+
# use the primary artists that are associated with the main work
|
|
86
|
+
artist_array = build_artist_array(track["artists"])
|
|
87
|
+
stmts.concat(build_track_artists(artist_array, w_count))
|
|
88
|
+
stmts.concat(build_track_extraartists(track["extraartists"], w_count)) if track["extraartists"].present?
|
|
89
|
+
w_count += 1
|
|
90
|
+
end
|
|
91
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# If the tracklist does not include the primary artists, use the ones defined at the master or release level
|
|
95
|
+
# @param [Array] artists from discogs
|
|
96
|
+
# @return [Array] either the tracklist "artists" or the primary artists
|
|
97
|
+
def build_artist_array(artists)
|
|
98
|
+
artists.present? ? artists : primary_artists
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# @param [Hash] discogs artists associated with the main work (master or release)
|
|
102
|
+
# @param [Integer] used to give unique URIS for works
|
|
103
|
+
# @return [Array] rdf statements
|
|
104
|
+
def build_secondary_works(track, w_count)
|
|
105
|
+
stmts = []
|
|
106
|
+
stmts << contruct_stmt_uri_object("Work1", "http://id.loc.gov/ontologies/bibframe/hasPart", "Work#{w_count}")
|
|
107
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Work")
|
|
108
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Audio")
|
|
109
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}", "http://id.loc.gov/ontologies/bibframe/title", "Work#{w_count}Title")
|
|
110
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}Title", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Title")
|
|
111
|
+
stmts << contruct_stmt_literal_object("Work#{w_count}Title", bf_main_title_predicate, track["title"])
|
|
112
|
+
stmts << contruct_stmt_literal_object("Work#{w_count}", "http://id.loc.gov/ontologies/bibframe/duration", track["duration"]) if track["duration"].present?
|
|
113
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @param [Array] discogs artists associated with the main work (master or release)
|
|
117
|
+
# @param [Integer] used to give unique URIS for works and primary contributions
|
|
118
|
+
# @return [Array] rdf statements
|
|
119
|
+
def build_track_artists(artists, w_count)
|
|
120
|
+
stmts = []
|
|
121
|
+
count = 1
|
|
122
|
+
artists.each do |artist|
|
|
123
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}", "http://id.loc.gov/ontologies/bibframe/contribution", "Work#{w_count}PrimaryContribution#{count}")
|
|
124
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}PrimaryContribution#{count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bflc/PrimaryContribution")
|
|
125
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}PrimaryContribution#{count}", bf_agent_predicate, artist["name"])
|
|
126
|
+
stmts << contruct_stmt_uri_object(artist["name"], rdf_type_predicate, bf_agent_type_object)
|
|
127
|
+
count += 1
|
|
128
|
+
end
|
|
129
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @param [Array] discogs extraartists associated with a track
|
|
133
|
+
# @param [Integer] used to give unique URIS for works, contributions and roles
|
|
134
|
+
# @return [Array] rdf statements
|
|
135
|
+
def build_track_extraartists(extraartists, w_count)
|
|
136
|
+
stmts = []
|
|
137
|
+
# to distinguish among contributors to a track/work and their roles
|
|
138
|
+
count = 1
|
|
139
|
+
extraartists.each do |artist|
|
|
140
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}", "http://id.loc.gov/ontologies/bibframe/contribution", "Work#{w_count}Contribution#{count}")
|
|
141
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}Contribution#{count}", rdf_type_predicate, "http://id.loc.gov/ontologies/bibframe/Contribution")
|
|
142
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}Contribution#{count}", bf_agent_predicate, artist["name"])
|
|
143
|
+
stmts << contruct_stmt_uri_object(artist["name"], rdf_type_predicate, bf_agent_type_object)
|
|
144
|
+
stmts << contruct_stmt_uri_object(artist["name"], bf_role_predicate, "Work#{w_count}ContributorRole#{count}")
|
|
145
|
+
stmts << contruct_stmt_uri_object("Work#{w_count}ContributorRole#{count}", rdf_type_predicate, bf_role_type_object)
|
|
146
|
+
stmts << contruct_stmt_literal_object("Work#{w_count}ContributorRole#{count}", rdfs_label_predicate, artist["role"])
|
|
147
|
+
count += 1
|
|
148
|
+
end
|
|
149
|
+
stmts # w/out this line, building the graph throws an undefined method `graph_name=' error
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|