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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/controllers/qa/linked_data_terms_controller.rb +30 -9
  4. data/app/controllers/qa/terms_controller.rb +3 -2
  5. data/app/models/qa/linked_data/config/context_property_map.rb +6 -25
  6. data/app/services/qa/iri_template_service.rb +32 -24
  7. data/app/services/qa/linked_data/authority_service.rb +8 -0
  8. data/app/services/qa/linked_data/authority_url_service.rb +27 -8
  9. data/app/services/qa/linked_data/deep_sort_service.rb +3 -2
  10. data/app/services/qa/linked_data/graph_service.rb +13 -0
  11. data/app/services/qa/linked_data/language_service.rb +12 -0
  12. data/app/services/qa/linked_data/language_sort_service.rb +7 -2
  13. data/app/services/qa/linked_data/ldpath_service.rb +40 -0
  14. data/app/services/qa/linked_data/mapper/graph_ldpath_mapper_service.rb +49 -0
  15. data/app/services/qa/linked_data/mapper/graph_mapper_service.rb +3 -11
  16. data/app/services/qa/linked_data/mapper/graph_predicate_mapper_service.rb +40 -0
  17. data/app/services/qa/linked_data/mapper/search_results_mapper_service.rb +58 -11
  18. data/app/services/qa/linked_data/mapper/term_results_mapper_service.rb +80 -0
  19. data/config/authorities/linked_data/loc.json +13 -7
  20. data/config/authorities/linked_data/oclc_fast.json +13 -8
  21. data/lib/generators/qa/discogs/USAGE +10 -0
  22. data/lib/generators/qa/discogs/discogs_generator.rb +12 -0
  23. data/lib/generators/qa/discogs/templates/config/discogs-formats.yml +346 -0
  24. data/lib/generators/qa/discogs/templates/config/discogs-genres.yml +627 -0
  25. data/lib/generators/qa/install/templates/config/initializers/qa.rb +4 -0
  26. data/lib/qa.rb +6 -0
  27. data/lib/qa/authorities.rb +2 -0
  28. data/lib/qa/authorities/discogs.rb +28 -0
  29. data/lib/qa/authorities/discogs/discogs_instance_builder.rb +145 -0
  30. data/lib/qa/authorities/discogs/discogs_translation.rb +126 -0
  31. data/lib/qa/authorities/discogs/discogs_utils.rb +89 -0
  32. data/lib/qa/authorities/discogs/discogs_works_builder.rb +153 -0
  33. data/lib/qa/authorities/discogs/generic_authority.rb +151 -0
  34. data/lib/qa/authorities/discogs_subauthority.rb +9 -0
  35. data/lib/qa/authorities/linked_data/config.rb +7 -3
  36. data/lib/qa/authorities/linked_data/config/search_config.rb +99 -11
  37. data/lib/qa/authorities/linked_data/config/term_config.rb +112 -8
  38. data/lib/qa/authorities/linked_data/find_term.rb +154 -84
  39. data/lib/qa/authorities/linked_data/search_query.rb +76 -13
  40. data/lib/qa/configuration.rb +8 -0
  41. data/lib/qa/version.rb +1 -1
  42. data/spec/controllers/linked_data_terms_controller_spec.rb +151 -30
  43. data/spec/controllers/terms_controller_spec.rb +4 -0
  44. data/spec/features/linked_data/language_spec.rb +298 -0
  45. data/spec/fixtures/authorities/linked_data/lod_full_config.json +21 -5
  46. data/spec/fixtures/authorities/linked_data/lod_lang_defaults.json +4 -4
  47. data/spec/fixtures/authorities/linked_data/lod_lang_multi_defaults.json +4 -4
  48. data/spec/fixtures/authorities/linked_data/lod_lang_no_defaults.json +4 -5
  49. data/spec/fixtures/authorities/linked_data/lod_lang_param.json +4 -4
  50. data/spec/fixtures/authorities/linked_data/lod_term_uri_param_config.json +1 -1
  51. data/spec/fixtures/discogs-find-response-json.json +1 -0
  52. data/spec/fixtures/discogs-find-response-jsonld-master.json +1 -0
  53. data/spec/fixtures/discogs-find-response-jsonld-release.json +1 -0
  54. data/spec/fixtures/discogs-id-matches-master.json +1 -0
  55. data/spec/fixtures/discogs-id-matches-release.json +1 -0
  56. data/spec/fixtures/discogs-id-not-found-master.json +1 -0
  57. data/spec/fixtures/discogs-id-not-found-release.json +1 -0
  58. data/spec/fixtures/discogs-search-response-no-auth.json +1 -0
  59. data/spec/fixtures/discogs-search-response-no-subauth.json +1 -0
  60. data/spec/fixtures/discogs-search-response-subauth.json +1 -0
  61. data/spec/fixtures/lod_lang_search_enesfrde.rdf.xml +60 -0
  62. data/spec/fixtures/lod_lang_search_sv.rdf.xml +42 -0
  63. data/spec/fixtures/lod_loc_term_found.rdf.xml +5 -0
  64. data/spec/lib/authorities/discogs/generic_authority_spec.rb +235 -0
  65. data/spec/lib/authorities/discogs_spec.rb +17 -0
  66. data/spec/lib/authorities/linked_data/config_spec.rb +68 -5
  67. data/spec/lib/authorities/linked_data/find_term_spec.rb +298 -3
  68. data/spec/lib/authorities/linked_data/generic_authority_spec.rb +46 -485
  69. data/spec/lib/authorities/linked_data/search_config_spec.rb +154 -3
  70. data/spec/lib/authorities/linked_data/search_query_spec.rb +240 -3
  71. data/spec/lib/authorities/linked_data/term_config_spec.rb +193 -5
  72. data/spec/lib/configuration_spec.rb +18 -0
  73. data/spec/models/linked_data/config/context_property_map_spec.rb +3 -31
  74. data/spec/services/iri_template_service_spec.rb +54 -12
  75. data/spec/{lib/authorities → services}/linked_data/authority_service_spec.rb +47 -0
  76. data/spec/services/linked_data/language_service_spec.rb +52 -11
  77. data/spec/services/linked_data/ldpath_service_spec.rb +61 -0
  78. data/spec/services/linked_data/mapper/graph_ldpath_mapper_service_spec.rb +118 -0
  79. data/spec/services/linked_data/mapper/graph_predicate_mapper_service_spec.rb +110 -0
  80. data/spec/services/linked_data/mapper/term_results_mapper_service_spec.rb +94 -0
  81. data/spec/spec_helper.rb +1 -1
  82. data/spec/support/matchers/include_hash.rb +5 -0
  83. data/spec/test_app_templates/lib/generators/test_app_generator.rb +4 -0
  84. metadata +73 -5
  85. 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
 
@@ -21,4 +21,6 @@ module Qa::Authorities
21
21
  autoload :AssignFastSubauthority
22
22
  autoload :Crossref
23
23
  autoload :LinkedData
24
+ autoload :Discogs
25
+ autoload :DiscogsSubauthority
24
26
  end
@@ -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