qa 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|