Last active
July 6, 2021 15:54
-
-
Save mbklein/462912e9ec12efefdf3c6d126804d47f to your computer and use it in GitHub Desktop.
NU's IR DOI Minting Service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DOI | |
class Error < StandardError | |
attr_reader :response | |
def initialize(response) | |
@response = response | |
end | |
def to_s | |
"#{response.status} #{response.reason_phrase}\n\t#{response.body}" | |
end | |
end | |
attr_reader :document | |
delegate :client, :server_reachable?, to: :class | |
delegate :attributes, :attributes=, to: :document | |
TOMBSTONE_URL = 'https://www.datacite.org/invalid.html'.freeze | |
PERMITTED_ATTRIBUTES = %w[ | |
bibtex bodyhasPid citationDoi citeproc contentType contentUrl contributors creators | |
dates dcIdentifier descriptions doi downloadLatency error event firstPage formats | |
fundingReferences geoLocations hasSchemaOrg identifier identifierType identifiers isActive | |
issue landingPage language lastPage metadataVersion prefix publicationYear publisher | |
redirectCount redirectUrls relatedIdentifiers resourceType rightsList ris schemaOrg schemaOrgid | |
schemaVersion sizes source state subjects suffix title titles types url url version volume | |
].freeze | |
REJECTABLE_VALUES = [nil, '', 0, [], {}].freeze | |
class << self | |
def client | |
return nil unless configured? | |
if @client.nil? | |
uri_klass = (Settings.doi_credentials.use_ssl ? URI::HTTPS : URI::HTTP) | |
datacite_uri = uri_klass.build(host: Settings.doi_credentials.host, port: Settings.doi_credentials.port, path: '/') | |
@client = Faraday.new(url: datacite_uri).tap do |conn| | |
conn.basic_auth(Settings.doi_credentials.user, Settings.doi_credentials.password) | |
end | |
end | |
@client | |
end | |
def configured? | |
Settings.doi_credentials.host.present? | |
end | |
def server_reachable? | |
client.get('heartbeat').status == 200 | |
rescue | |
false | |
end | |
end | |
def initialize(id = nil) | |
@document = Hashie::Mash.new(id: id.to_s.delete('doi:'), type: 'dois') | |
reload | |
end | |
def id | |
return nil if document.id.blank? | |
['doi', document.id].join(':') | |
end | |
def reload | |
return if id.nil? | |
load_from(client.get("dois/#{document.id}")) | |
end | |
def register | |
attributes.prefix ||= Settings.doi_credentials.default_shoulder | |
attributes.delete('event') | |
load_from(client.post('dois', to_json, content_type: 'application/vnd.api+json')) | |
end | |
alias register! register | |
def save | |
load_from(client.put("dois/#{document.id}", to_json, content_type: 'application/vnd.api+json')) | |
end | |
def hide! | |
document.id.blank? ? register : event('hide') | |
end | |
def publish! | |
register if document.id.blank? | |
event('publish') | |
end | |
def tombstone! | |
attributes.url = TOMBSTONE_URL | |
save | |
end | |
def to_json | |
{ 'data' => document }.to_json | |
end | |
private | |
def event(state) | |
attributes.event = state | |
save | |
end | |
def load_from(response) | |
raise Error, response unless response.success? | |
@document = Hashie::Mash.new(JSON.parse(response.body)).data.tap do |result| | |
result.delete('relationships') | |
result.attributes.select! { |k, v| PERMITTED_ATTRIBUTES.include?(k) && !REJECTABLE_VALUES.include?(v) } | |
end | |
self | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DoiMintingService | |
include Rails.application.routes.url_helpers | |
include ActionDispatch::Routing::PolymorphicRoutes | |
attr_reader :work, :doi | |
RESOURCE_TYPE_MAP = { | |
'Audio' => 'Sound', | |
'Book' => 'Text', | |
'Dataset' => 'Dataset', | |
'Dissertation' => 'Text', | |
'Image' => 'Image', | |
'Article' => 'Text', | |
'Masters Thesis' => 'Text', | |
'Part of Book' => 'Text', | |
'Poster' => 'Text', | |
'Report' => 'Text', | |
'Research Paper' => 'Text', | |
'Video' => 'Audiovisual', | |
'Software or Program Code' => 'Software' | |
}.freeze | |
def self.mint_identifier_for(work) | |
DoiMintingService.new(work).run | |
rescue => err | |
Rails.logger.warn("DOI not assigned for work #{work.id}: #{err.message}") | |
Honeybadger.notify(err) | |
end | |
def self.tombstone_identifier_for(work) | |
DoiMintingService.new(work).tombstone | |
rescue => err | |
Rails.logger.warn("DOI #{work.doi} for work #{work.id} not tombstoned: #{err.message}") | |
Honeybadger.notify(err) | |
end | |
def initialize(obj) | |
@work = obj | |
@doi = DOI.new(obj.doi) | |
end | |
def run | |
return unless DOI.configured? && DOI.server_reachable? | |
doi.attributes = metadata | |
doi.publish!.tap do |result| | |
if work.doi.nil? | |
work.doi = result.id | |
work.save | |
end | |
end | |
end | |
def tombstone | |
return unless DOI.configured? && DOI.server_reachable? | |
doi.tombstone! | |
end | |
private | |
def metadata | |
Hashie::Mash.new( | |
doi: work.doi, | |
creators: creators, | |
titles: titles, | |
publisher: publishers, | |
dates: [dates_created].flatten, | |
publicationYear: work.create_date.year, | |
types: resource_type, | |
url: url | |
) | |
end | |
def list_or_unknown(value) | |
value.empty? ? ['Unknown'] : value | |
end | |
def creators | |
list_or_unknown(work.creator).collect do |v| | |
{ name: v, nameIdentifiers: [{}] } | |
end | |
end | |
def titles | |
list_or_unknown(work.title).collect do |v| | |
{ title: v, lang: 'und' } | |
end | |
end | |
def publishers | |
list_or_unknown(work.publisher).join('; ') | |
end | |
def dates_created | |
list_or_unknown(work.date_created).collect do |v| | |
{ date: v, dateType: 'Created' } | |
end | |
end | |
def resource_type | |
{ | |
resourceTypeGeneral: RESOURCE_TYPE_MAP.fetch(work.resource_type.first, 'Other'), | |
resourceType: work.resource_type.first | |
} | |
end | |
def url | |
polymorphic_url(work, host: Settings.arch.host) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment