bolognese 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +56 -0
  3. data/.travis.yml +23 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +118 -0
  6. data/LICENSE +21 -0
  7. data/README.md +3 -0
  8. data/Rakefile +12 -0
  9. data/bin/bolognese +5 -0
  10. data/bolognese.gemspec +40 -0
  11. data/lib/bolognese.rb +12 -0
  12. data/lib/bolognese/author_utils.rb +61 -0
  13. data/lib/bolognese/cli.rb +38 -0
  14. data/lib/bolognese/crossref.rb +202 -0
  15. data/lib/bolognese/datacite.rb +157 -0
  16. data/lib/bolognese/date_utils.rb +48 -0
  17. data/lib/bolognese/doi_utils.rb +48 -0
  18. data/lib/bolognese/github.rb +106 -0
  19. data/lib/bolognese/metadata.rb +30 -0
  20. data/lib/bolognese/orcid.rb +24 -0
  21. data/lib/bolognese/pid_utils.rb +23 -0
  22. data/lib/bolognese/pubmed.rb +34 -0
  23. data/lib/bolognese/string.rb +5 -0
  24. data/lib/bolognese/utils.rb +27 -0
  25. data/lib/bolognese/version.rb +3 -0
  26. data/spec/cli_spec.rb +37 -0
  27. data/spec/crossref_spec.rb +113 -0
  28. data/spec/datacite_spec.rb +49 -0
  29. data/spec/doi_spec.rb +89 -0
  30. data/spec/fixtures/crossref.xml +742 -0
  31. data/spec/fixtures/datacite.xml +40 -0
  32. data/spec/fixtures/vcr_cassettes/Bolognese_CLI/read/crossref/as_crossref.yml +760 -0
  33. data/spec/fixtures/vcr_cassettes/Bolognese_CLI/read/crossref/as_schema_org.yml +1476 -0
  34. data/spec/fixtures/vcr_cassettes/Bolognese_CLI/read/datacite/as_datacite.yml +214 -0
  35. data/spec/fixtures/vcr_cassettes/Bolognese_CLI/read/datacite/as_schema_org.yml +384 -0
  36. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/doi_registration_agency/crossref.yml +44 -0
  37. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/doi_registration_agency/datacite.yml +44 -0
  38. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/doi_registration_agency/medra.yml +44 -0
  39. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/doi_registration_agency/not_found.yml +44 -0
  40. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_metadata/DOI_test.yml +843 -0
  41. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_metadata/DOI_with_SICI_DOI.yml +277 -0
  42. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_metadata/DOI_with_data_citation.yml +15755 -0
  43. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_metadata/date_in_future.yml +2691 -0
  44. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_metadata/journal_article.yml +1857 -0
  45. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_metadata/not_found_error.yml +93 -0
  46. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_metadata/posted_content.yml +5715 -0
  47. data/spec/fixtures/vcr_cassettes/Bolognese_Datacite/get_metadata/BlogPosting.yml +307 -0
  48. data/spec/fixtures/vcr_cassettes/Bolognese_Datacite/get_metadata/Dataset.yml +343 -0
  49. data/spec/fixtures/vcr_cassettes/Bolognese_Metadata/find_PID_provider/crossref.yml +44 -0
  50. data/spec/fixtures/vcr_cassettes/Bolognese_Metadata/find_PID_provider/crossref_doi_not_url.yml +44 -0
  51. data/spec/fixtures/vcr_cassettes/Bolognese_Metadata/find_PID_provider/datacite.yml +44 -0
  52. data/spec/fixtures/vcr_cassettes/Bolognese_Metadata/find_PID_provider/datacite_doi_http.yml +44 -0
  53. data/spec/fixtures/vcr_cassettes/Bolognese_Metadata/find_PID_provider/orcid.yml +44 -0
  54. data/spec/metadata_spec.rb +35 -0
  55. data/spec/orcid_spec.rb +23 -0
  56. data/spec/spec_helper.rb +88 -0
  57. metadata +419 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3a6b175047cc9fe1cae1eba94b41164dd0c9e406
4
+ data.tar.gz: f78c9e066599d1a2b6db5c66cc1a19e8024dd73b
5
+ SHA512:
6
+ metadata.gz: fede61f6811145761b605513ae01345508e8f60a7e2d807d22ac17fd80096326d7ab4af1a8b3ca117c1856adb2f31012a23ed3392023d8f9213b424d32c2e459
7
+ data.tar.gz: 4ddc0ca66cda0fbb1ea3d10021d97b1f532f733dd221c39509d1994a4c51da09f27da515fdb0536d086d425d3d0075a00fb6f498e9813802e19b0bcde70cbb87
@@ -0,0 +1,56 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
51
+
52
+ coverage/
53
+ .env
54
+ .env.*
55
+ !.env.example
56
+ !.env.travis
@@ -0,0 +1,23 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.3
4
+
5
+ before_install:
6
+ - export TZ=Europe/Berlin
7
+
8
+ install:
9
+ - travis_retry bundle install
10
+
11
+ script: bundle exec rspec
12
+
13
+ notifications:
14
+ email: false
15
+
16
+ deploy:
17
+ provider: rubygems
18
+ api_key:
19
+ secure: jNV7zS0e1cSV+oVCqRyolo16QHJFgba7vnFnbG1Nb4Xk3p/hQnE/WTvV0up7jXE9HVKDmMTPPVUfdtvyE/xsTlWNpjIaQ5NdAz+wf4fTkwN8in8AHvxL0V1Zj202cgn6Ikoy2Zwkg4eZTxo1PJ6niyyfMHd/XpaW7zRd7BBN3901DeR0qCsYTJaFF8cO5d/CeKBn0aCHv7vzqRvm+NsEgVmfNCgjQAvMfDZChrlYzUZlOpcmh01u2z0vZyRmsidmAehLRF6GcZpoL8CVBbyzHr6EVjOrI2XVikwj17j4hPLcEzB/gGyK6OUX4JuiSxdRYxAPApZY96SUbFFrfIDusHv1csgwznAlEF4VgerpHq8C5B+CylXHjreT17b5U2QBsr79z401a6m6aC+RM/RDu2lW1p93f/fAK/znBO3DyodlvmVYGN16PuA7g7pa8LYKwtZGSQmTlbAycwcbKHgvbdKmxYAFTOE6CWeginLM7bvOmF6IHcNGzBHK1I8xES0y1XQHFKYl4nT3cMEsA6aRRRQCIQwbr6FE8gyPZrs5P/W9vPM9p6cryXFaSJWbGbounr6lqQgtCmfDdqXPxAszTbcBu/yBukxeM3KvRVrsbtuIanuHn1ppvA31M4bUGTP9CfwKojE9bdo6m7nfHTpjNy7eqOPuUH33cQhIPMCvpZ4=
20
+ gem: bolognese
21
+ on:
22
+ tags: true
23
+ repo: datacite/bolognese
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,118 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ Bolognese (0.2)
5
+ activesupport (~> 4.2, >= 4.2.5)
6
+ builder (~> 3.2, >= 3.2.2)
7
+ dotenv (~> 2.1, >= 2.1.1)
8
+ maremma (~> 3.5)
9
+ namae (~> 0.10.2)
10
+ nokogiri (~> 1.6, >= 1.6.8)
11
+ postrank-uri (~> 1.0, >= 1.0.18)
12
+ thor (~> 0.19)
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ activesupport (4.2.7.1)
18
+ i18n (~> 0.7)
19
+ json (~> 1.7, >= 1.7.7)
20
+ minitest (~> 5.1)
21
+ thread_safe (~> 0.3, >= 0.3.4)
22
+ tzinfo (~> 1.1)
23
+ addressable (2.3.8)
24
+ builder (3.2.3)
25
+ codeclimate-test-reporter (1.0.5)
26
+ simplecov
27
+ crack (0.4.3)
28
+ safe_yaml (~> 1.0.0)
29
+ diff-lcs (1.3)
30
+ docile (1.1.5)
31
+ dotenv (2.2.0)
32
+ excon (0.45.4)
33
+ faraday (0.9.2)
34
+ multipart-post (>= 1.2, < 3)
35
+ faraday-encoding (0.0.4)
36
+ faraday
37
+ faraday_middleware (0.10.1)
38
+ faraday (>= 0.7.4, < 1.0)
39
+ hashdiff (0.3.2)
40
+ i18n (0.8.0)
41
+ json (1.8.6)
42
+ maremma (3.5)
43
+ activesupport (~> 4.2, >= 4.2.5)
44
+ addressable (>= 2.3.6)
45
+ builder (~> 3.2, >= 3.2.2)
46
+ excon (~> 0.45.0)
47
+ faraday (~> 0.9.2)
48
+ faraday-encoding (~> 0.0.1)
49
+ faraday_middleware (~> 0.10.0)
50
+ multi_json (~> 1.11.2)
51
+ nokogiri (~> 1.6.7)
52
+ oj (~> 2.18, >= 2.18.1)
53
+ mini_portile2 (2.1.0)
54
+ minitest (5.10.1)
55
+ multi_json (1.11.3)
56
+ multipart-post (2.0.0)
57
+ namae (0.10.2)
58
+ nokogiri (1.6.8.1)
59
+ mini_portile2 (~> 2.1.0)
60
+ oj (2.18.1)
61
+ postrank-uri (1.0.18)
62
+ addressable (~> 2.3.0)
63
+ nokogiri (~> 1.6.1)
64
+ public_suffix (~> 1.1.3)
65
+ public_suffix (1.1.3)
66
+ rack (2.0.1)
67
+ rack-test (0.6.3)
68
+ rack (>= 1.0)
69
+ rake (12.0.0)
70
+ rspec (3.5.0)
71
+ rspec-core (~> 3.5.0)
72
+ rspec-expectations (~> 3.5.0)
73
+ rspec-mocks (~> 3.5.0)
74
+ rspec-core (3.5.4)
75
+ rspec-support (~> 3.5.0)
76
+ rspec-expectations (3.5.0)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.5.0)
79
+ rspec-mocks (3.5.0)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.5.0)
82
+ rspec-support (3.5.0)
83
+ rspec-xsd (0.1.0)
84
+ nokogiri (~> 1.6)
85
+ rspec (~> 3)
86
+ safe_yaml (1.0.4)
87
+ simplecov (0.12.0)
88
+ docile (~> 1.1.0)
89
+ json (>= 1.8, < 3)
90
+ simplecov-html (~> 0.10.0)
91
+ simplecov-html (0.10.0)
92
+ thor (0.19.4)
93
+ thread_safe (0.3.5)
94
+ tzinfo (1.2.2)
95
+ thread_safe (~> 0.1)
96
+ vcr (3.0.3)
97
+ webmock (1.24.6)
98
+ addressable (>= 2.3.6)
99
+ crack (>= 0.3.2)
100
+ hashdiff
101
+
102
+ PLATFORMS
103
+ ruby
104
+
105
+ DEPENDENCIES
106
+ Bolognese!
107
+ bundler (~> 1.0)
108
+ codeclimate-test-reporter (~> 1.0, >= 1.0.0)
109
+ rack-test (~> 0)
110
+ rake (~> 12.0)
111
+ rspec (~> 3.4)
112
+ rspec-xsd (~> 0.1.0)
113
+ simplecov (~> 0.12.0)
114
+ vcr (~> 3.0, >= 3.0.3)
115
+ webmock (~> 1.22, >= 1.22.3)
116
+
117
+ BUNDLED WITH
118
+ 1.12.5
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 DataCite
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ # bolognese
2
+
3
+ Command-line utility for conversion of DOI metadata.
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler'
4
+ require 'rake'
5
+ require 'yaml'
6
+ require 'rspec/core/rake_task'
7
+
8
+ Bundler::GemHelper.install_tasks
9
+ RSpec::Core::RakeTask.new('spec')
10
+
11
+ # default task is running rspec tests
12
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path("../../lib/bolognese", __FILE__)
4
+
5
+ Bolognese::CLI.start
@@ -0,0 +1,40 @@
1
+ require "date"
2
+ require File.expand_path("../lib/bolognese/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.authors = "Martin Fenner"
6
+ s.email = "mfenner@datacite.org"
7
+ s.name = "bolognese"
8
+ s.homepage = "https://github.com/datacite/bolognese"
9
+ s.summary = "Ruby client library for conversion of DOI Metadata"
10
+ s.date = Date.today
11
+ s.description = "Convert DOI metadata to and from Crossref and DataCite XML, as well as schema.org/JSON-LD"
12
+ s.require_paths = ["lib"]
13
+ s.version = Bolognese::VERSION
14
+ s.extra_rdoc_files = ["README.md"]
15
+ s.license = 'MIT'
16
+
17
+ # Declary dependencies here, rather than in the Gemfile
18
+ s.add_dependency 'maremma', '~> 3.5'
19
+ s.add_dependency 'nokogiri', '~> 1.6', '>= 1.6.8'
20
+ s.add_dependency 'builder', '~> 3.2', '>= 3.2.2'
21
+ s.add_dependency 'activesupport', '~> 4.2', '>= 4.2.5'
22
+ s.add_dependency 'dotenv', '~> 2.1', '>= 2.1.1'
23
+ s.add_dependency 'thor', '~> 0.19'
24
+ s.add_dependency 'namae', '~> 0.10.2'
25
+ s.add_dependency 'postrank-uri', '~> 1.0', '>= 1.0.18'
26
+ s.add_development_dependency 'bundler', '~> 1.0'
27
+ s.add_development_dependency 'rspec', '~> 3.4'
28
+ s.add_development_dependency 'rspec-xsd', '~> 0.1.0'
29
+ s.add_development_dependency 'rake', '~> 12.0'
30
+ s.add_development_dependency 'rack-test', '~> 0'
31
+ s.add_development_dependency 'vcr', '~> 3.0', '>= 3.0.3'
32
+ s.add_development_dependency 'webmock', '~> 1.22', '>= 1.22.3'
33
+ s.add_development_dependency 'codeclimate-test-reporter', '~> 1.0', '>= 1.0.0'
34
+ s.add_development_dependency 'simplecov', '~> 0.12.0'
35
+
36
+ s.require_paths = ["lib"]
37
+ s.files = `git ls-files`.split($/)
38
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
39
+ s.executables = ["bolognese"]
40
+ end
@@ -0,0 +1,12 @@
1
+ require 'active_support/all'
2
+ require 'nokogiri'
3
+ require 'maremma'
4
+ require 'postrank-uri'
5
+
6
+ require "bolognese/version"
7
+ require "bolognese/metadata"
8
+ require "bolognese/crossref"
9
+ require "bolognese/datacite"
10
+ require "bolognese/orcid"
11
+ require "bolognese/cli"
12
+ require "bolognese/string"
@@ -0,0 +1,61 @@
1
+ require 'namae'
2
+
3
+ module Bolognese
4
+ module AuthorUtils
5
+ # only assume personal name when using sort-order: "Turing, Alan"
6
+ def get_one_author(author, options = {})
7
+ orcid = get_name_identifier(author)
8
+ author = author.fetch("creatorName", nil)
9
+
10
+ return { "Name" => "" } if author.strip.blank?
11
+
12
+ author = cleanup_author(author)
13
+ names = Namae.parse(author)
14
+
15
+ if names.blank? || is_personal_name?(author).blank?
16
+ { "@type" => "Agent",
17
+ "@id" => orcid,
18
+ "Name" => author }.compact
19
+ else
20
+ name = names.first
21
+
22
+ { "@type" => "Person",
23
+ "@id" => orcid,
24
+ "givenName" => name.given,
25
+ "familyName" => name.family }.compact
26
+ end
27
+ end
28
+
29
+ def cleanup_author(author)
30
+ # detect pattern "Smith J.", but not "Smith, John K."
31
+ author = author.gsub(/[[:space:]]([A-Z]\.)?(-?[A-Z]\.)$/, ', \1\2') unless author.include?(",")
32
+
33
+ # titleize strings
34
+ # remove non-standard space characters
35
+ author.my_titleize
36
+ .gsub(/[[:space:]]/, ' ')
37
+ end
38
+
39
+ def is_personal_name?(author)
40
+ return true if author.include?(",")
41
+
42
+ # lookup given name
43
+ #::NameDetector.name_exists?(author.split.first)
44
+ end
45
+
46
+ # parse array of author strings into CSL format
47
+ def get_authors(authors, options={})
48
+ Array(authors).map { |author| get_one_author(author, options) }
49
+ end
50
+
51
+ def get_name_identifier(author)
52
+ name_identifier = author.dig("nameIdentifier", "text")
53
+ name_identifier_scheme = author.dig("nameIdentifier", "nameIdentifierScheme") || "ORCID"
54
+ if name_identifier_scheme.downcase == "orcid" && name_identifier = validate_orcid(name_identifier)
55
+ "http://orcid.org/#{name_identifier}"
56
+ else
57
+ nil
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+
3
+ require "thor"
4
+
5
+ module Bolognese
6
+ class CLI < Thor
7
+ def self.exit_on_failure?
8
+ true
9
+ end
10
+
11
+ # from http://stackoverflow.com/questions/22809972/adding-a-version-option-to-a-ruby-thor-cli
12
+ map %w[--version -v] => :__print_version
13
+
14
+ desc "--version, -v", "print the version"
15
+ def __print_version
16
+ puts Bolognese::VERSION
17
+ end
18
+
19
+ desc "read pid", "read metadata for PID"
20
+ method_option :as, :default => "schema_org"
21
+ def read(pid)
22
+ provider = Metadata.new(pid).provider
23
+
24
+ case
25
+ when provider == "crossref" && options[:as] == "crossref"
26
+ puts Crossref.new(pid).raw
27
+ when provider == "crossref"
28
+ puts Crossref.new(pid).as_schema_org
29
+ when provider == "datacite" && options[:as] == "datacite"
30
+ puts Datacite.new(pid).raw
31
+ when "datacite"
32
+ puts Datacite.new(pid).as_schema_org
33
+ else
34
+ puts "not implemented"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,202 @@
1
+ require_relative 'doi_utils'
2
+ require_relative 'utils'
3
+
4
+ module Bolognese
5
+ class Crossref < Metadata
6
+ include Bolognese::DoiUtils
7
+ include Bolognese::Utils
8
+
9
+ # CrossRef types from https://api.crossref.org/types
10
+ CROSSREF_TYPE_TRANSLATIONS = {
11
+ "Proceedings" => nil,
12
+ "ReferenceBook" => nil,
13
+ "JournalIssue" => nil,
14
+ "ProceedingsArticle" => nil,
15
+ "Other" => nil,
16
+ "Dissertation" => "Thesis",
17
+ "Dataset" => "Dataset",
18
+ "EditedBook" => "Book",
19
+ "JournalArticle" => "ScholarlyArticle",
20
+ "Journal" => nil,
21
+ "Report" => nil,
22
+ "BookSeries" => nil,
23
+ "ReportSeries" => nil,
24
+ "BookTrack" => nil,
25
+ "Standard" => nil,
26
+ "BookSection" => nil,
27
+ "BookPart" => nil,
28
+ "Book" => "Book",
29
+ "BookChapter" => "Chapter",
30
+ "StandardSeries" => nil,
31
+ "Monograph" => "Book",
32
+ "Component" => nil,
33
+ "ReferenceEntry" => nil,
34
+ "JournalVolume" => nil,
35
+ "BookSet" => nil,
36
+ "PostedContent" => nil
37
+ }
38
+
39
+ attr_reader = :id, :metadata, :schema_org
40
+
41
+ def initialize(doi)
42
+ @id = normalize_doi(doi)
43
+ end
44
+
45
+ def raw
46
+ response = Maremma.get(@id, accept: "application/vnd.crossref.unixref+xml", host: true, raw: true)
47
+ @raw ||= response.body.fetch("data", nil)
48
+ end
49
+
50
+ def metadata
51
+ @metadata ||= raw.present? ? Maremma.from_xml(raw).fetch("doi_records", {}).fetch("doi_record", {}) : {}
52
+ end
53
+
54
+ def exists?
55
+ metadata.present?
56
+ end
57
+
58
+ def journal_metadata
59
+ metadata.dig("crossref", "journal", "journal_metadata").presence || {}
60
+ end
61
+
62
+ def bibliographic_metadata
63
+ if metadata.dig("crossref", "journal", "journal_article").present?
64
+ metadata.dig("crossref", "journal", "journal_article")
65
+ else
66
+ k = metadata.dig("crossref").keys.last
67
+ metadata.dig("crossref", k).presence || {}
68
+ end
69
+ end
70
+
71
+ def program_metadata
72
+ bibliographic_metadata.dig("program") ||
73
+ bibliographic_metadata.dig("crossmark", "custom_metadata", "program") || {}
74
+ end
75
+
76
+ def additional_type
77
+ if metadata.dig("crossref", "journal").present?
78
+ metadata.dig("crossref", "journal").keys.last.camelize
79
+ else
80
+ metadata.dig("crossref").keys.last.camelize
81
+ end
82
+ end
83
+
84
+ def type
85
+ CROSSREF_TYPE_TRANSLATIONS[additional_type] || "CreativeWork"
86
+ end
87
+
88
+ def name
89
+ parse_attribute(bibliographic_metadata.dig("titles", "title"))
90
+ end
91
+
92
+ def alternate_name
93
+ if bibliographic_metadata.fetch("publisher_item", nil).present?
94
+ parse_attribute(bibliographic_metadata.dig("publisher_item", "item_number"))
95
+ else
96
+ parse_attribute(bibliographic_metadata.fetch("item_number", nil))
97
+ end
98
+ end
99
+
100
+ def description
101
+ bibliographic_metadata.fetch("abstract", {}).values.first
102
+ end
103
+
104
+ def license
105
+ access_indicator = Array.wrap(program_metadata).find { |m| m["name"] == "AccessIndicators" }
106
+ if access_indicator.present?
107
+ parse_attribute(access_indicator["license_ref"])
108
+ else
109
+ nil
110
+ end
111
+ end
112
+
113
+ def author
114
+ person = bibliographic_metadata.dig("contributors", "person_name")
115
+ Array(person).select { |a| a["contributor_role"] == "author" }.map do |a|
116
+ { "@type" => "Person",
117
+ "@id" => a["ORCID"],
118
+ "givenName" => a["given_name"],
119
+ "familyName" => a["surname"] }.compact
120
+ end
121
+ end
122
+
123
+ def editor
124
+ person = bibliographic_metadata.dig("contributors", "person_name")
125
+ Array(person).select { |a| a["contributor_role"] == "editor" }.map do |a|
126
+ { "@type" => "Person",
127
+ "@id" => a["ORCID"],
128
+ "givenName" => a["given_name"],
129
+ "familyName" => a["surname"] }.compact
130
+ end.presence
131
+ end
132
+
133
+ def date_published
134
+ pub_date = bibliographic_metadata.fetch("publication_date", nil) ||
135
+ bibliographic_metadata.fetch("acceptance_date", nil)
136
+ if pub_date.present?
137
+ get_date_from_parts(pub_date["year"], pub_date["month"], pub_date["day"])
138
+ else
139
+ nil
140
+ end
141
+ end
142
+
143
+ def date_modified
144
+ Time.parse(metadata.fetch("timestamp", "")).utc.iso8601
145
+ end
146
+
147
+ def page_start
148
+ bibliographic_metadata.dig("pages", "first_page")
149
+ end
150
+
151
+ def page_end
152
+ bibliographic_metadata.dig("pages", "last_page")
153
+ end
154
+
155
+ def is_part_of
156
+ if journal_metadata.present?
157
+ { "@type" => "Periodical",
158
+ "name" => journal_metadata["full_title"],
159
+ "issn" => parse_attribute(journal_metadata.fetch("issn", nil)) }.compact
160
+ else
161
+ nil
162
+ end
163
+ end
164
+
165
+ def citation
166
+ citations = bibliographic_metadata.dig("citation_list", "citation")
167
+ Array(citations).map do |c|
168
+ { "@type" => "CreativeWork",
169
+ "@id" => normalize_doi(c["doi"]),
170
+ "position" => c["key"],
171
+ "name" => c["article_title"],
172
+ "datePublished" => c["cYear"] }.compact
173
+ end.presence
174
+ end
175
+
176
+ def provider
177
+ { "@type" => "Organization",
178
+ "name" => "Crossref" }
179
+ end
180
+
181
+ def as_schema_org
182
+ { "@context" => "http://schema.org",
183
+ "@type" => type,
184
+ "@id" => id,
185
+ "additionalType" => additional_type,
186
+ "name" => name,
187
+ "alternateName" => alternate_name,
188
+ "author" => author,
189
+ "editor" => editor,
190
+ "description" => description,
191
+ "license" => license,
192
+ "datePublished" => date_published,
193
+ "dateModified" => date_modified,
194
+ "pageStart" => page_start,
195
+ "pageEnd" => page_end,
196
+ "isPartOf" => is_part_of,
197
+ "citation" => citation,
198
+ "provider" => provider
199
+ }.compact
200
+ end
201
+ end
202
+ end