lib/orcid_client/work.rb
require 'active_support/all'
require 'nokogiri'
require_relative 'api'
require_relative 'author'
require_relative 'base'
require_relative 'date'
require_relative 'work_type'
module OrcidClient
class Work
include OrcidClient::Base
include OrcidClient::Author
include OrcidClient::Date
include OrcidClient::WorkType
include OrcidClient::Api
include Bolognese::Utils
attr_reader :doi, :orcid, :schema, :orcid_token, :sandbox, :put_code, :agency, :visibility, :validation_errors, :name_detector
attr_writer :visibility
def initialize(doi:, orcid:, orcid_token:, **options)
@doi = doi
@orcid = orcid
@orcid_token = orcid_token
@sandbox = options.fetch(:sandbox, nil) || ENV['API_URL'] == "https://api.stage.datacite.org"
@put_code = options.fetch(:put_code, nil)
@agency = options.fetch(:agency, nil)
@visibility = options.fetch(:visibility, 'public')
end
SCHEMA = File.expand_path("../../../resources/record_#{API_VERSION}/work-#{API_VERSION}.xsd", __FILE__)
# recognize given name. Can be loaded once as ::NameDetector, e.g. in a Rails initializer
def name_detector
@name_detector ||= defined?(::NameDetector) ? ::NameDetector : GenderDetector.new
end
def metadata
@metadata ||= Bolognese::Metadata.new(input: doi, sandbox: sandbox, from: agency)
end
def contributors
Array.wrap(metadata.creators).map do |contributor|
orcid = Array.wrap(contributor["nameIdentifiers"]).find { |c| c["nameIdentifierScheme"] == "ORCID" }.to_h.fetch("nameIdentifier", nil)
credit_name = contributor["familyName"].present? ? [contributor["givenName"], contributor["familyName"]].join(" ") : contributor["name"]
{ orcid: orcid,
credit_name: credit_name,
role: nil }.compact
end
end
def title
parse_attributes(metadata.titles, content: "title", first: true)
end
# user publisher name as fallback
def container_title
metadata.container_title || metadata.publisher.present? ? metadata.publisher["name"] : nil
end
def publication_date
pd = get_date(metadata.dates, "Issued") || metadata.publication_year
get_year_month_day(pd)
end
def description
ct = parse_attributes(metadata.descriptions, content: "description", first: true)
ct.squish if ct.present?
end
def type
orcid_work_type(metadata.types["resourceTypeGeneral"], metadata.types["resourceType"])
end
def has_required_elements?
doi && contributors && title && container_title && publication_date
end
def data
return nil unless has_required_elements?
Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.send(:'work:work', root_attributes) do
insert_work(xml)
end
end.to_xml
end
def insert_work(xml)
insert_titles(xml)
insert_description(xml)
insert_type(xml)
insert_pub_date(xml)
insert_ids(xml)
insert_contributors(xml)
end
def insert_titles(xml)
if title
xml.send(:'work:title') do
xml.send(:'common:title', title.truncate(1000, separator: ' '))
end
end
xml.send(:'work:journal-title', container_title) if container_title
end
def insert_description(xml)
return nil unless description.present?
xml.send(:'work:short-description', description.truncate(2500, separator: ' '))
end
def insert_type(xml)
xml.send(:'work:type', type)
end
def insert_pub_date(xml)
if publication_date['year']
xml.send(:'common:publication-date') do
xml.year(publication_date.fetch('year'))
xml.month(publication_date.fetch('month', nil)) if publication_date['month']
xml.day(publication_date.fetch('day', nil)) if publication_date['month'] && publication_date['day']
end
end
end
def insert_ids(xml)
xml.send(:'common:external-ids') do
insert_id(xml, 'doi', doi, 'self')
end
end
def insert_id(xml, id_type, value, relationship)
xml.send(:'common:external-id') do
xml.send(:'common:external-id-type', id_type)
xml.send(:'common:external-id-value', value)
xml.send(:'common:external-id-relationship', relationship)
end
end
def insert_contributors(xml)
return nil unless contributors.present?
xml.send(:'work:contributors') do
contributors.each do |contributor|
xml.contributor do
insert_contributor(xml, contributor)
end
end
end
end
def insert_contributor(xml, contributor)
if contributor[:orcid].present?
xml.send(:'common:contributor-orcid') do
xml.send(:'common:uri', contributor[:orcid])
xml.send(:'common:path', validate_orcid(contributor[:orcid]))
xml.send(:'common:host', 'orcid.org')
end
end
xml.send(:'credit-name', contributor[:credit_name])
if contributor[:role]
xml.send(:'contributor-attributes') do
xml.send(:'contributor-role', contributor[:role])
end
end
end
def without_control(s)
r = ''
s.each_codepoint do |c|
if c >= 32
r << c
end
end
r
end
def root_attributes
{ :'put-code' => put_code,
:'visibility' => visibility,
:'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
:'xsi:schemaLocation' => 'http://www.orcid.org/ns/work ../work-3.0.xsd',
:'xmlns:common' => 'http://www.orcid.org/ns/common',
:'xmlns:work' => 'http://www.orcid.org/ns/work' }.compact
end
def schema
Nokogiri::XML::Schema(open(SCHEMA))
end
def validation_errors
@validation_errors ||= schema.validate(Nokogiri::XML(data)).map { |error| error.to_s }
end
end
end