qa 4.0.0 → 4.1.0

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