Skip to content

Commit abd0064

Browse files
authored
Merge pull request #1146 from pan-training/oai_pmh
OAI-PMH 2.0 Implementation to export Materials
2 parents 5dc63a2 + 5a94a0d commit abd0064

11 files changed

Lines changed: 887 additions & 2 deletions

File tree

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ gem 'kt-paperclip'
3434
gem 'linkeddata'
3535
gem 'maxmind-db'
3636
gem 'money-rails'
37+
gem 'oai'
3738
gem 'omniauth_openid_connect'
3839
gem 'omniauth-rails_csrf_protection'
3940
gem 'pg'

Gemfile.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,11 @@ GEM
398398
mini_portile2 (~> 2.8.2)
399399
racc (~> 1.4)
400400
numerizer (0.1.1)
401+
oai (1.3.0)
402+
builder (>= 3.1.0)
403+
faraday (< 3)
404+
faraday-follow_redirects (>= 0.3.0, < 2)
405+
rexml
401406
omniauth (2.1.1)
402407
hashie (>= 3.4.6)
403408
rack (>= 2.2.3)
@@ -853,6 +858,7 @@ DEPENDENCIES
853858
minitest
854859
minitest-reporters
855860
money-rails
861+
oai
856862
omniauth-rails_csrf_protection
857863
omniauth_openid_connect
858864
pg

app/controllers/oai_controller.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# The controller for actions related to OAI-PMH
2+
class OaiController < ApplicationController
3+
# CSRF token authentication causes problems with OAI-PMH POST requests and OAI-PMH POST is safe because it returns static public content
4+
skip_before_action :verify_authenticity_token, only: [:index]
5+
6+
# GET /oai-pmh
7+
def index
8+
provider = TrainingProvider.new
9+
response = provider.process_request(oai_params.to_h)
10+
11+
# add XSLT prefix
12+
response.sub!(/<\?xml[^>]+\?>/, "\\0\n<?xml-stylesheet type=\"text/xsl\" href=\"/oai2xhtml.xsl\"?>")
13+
14+
render body: response, content_type: 'text/xml'
15+
end
16+
17+
private
18+
19+
def oai_params
20+
params.permit(:verb, :identifier, :metadataPrefix, :set, :from, :until, :resumptionToken)
21+
end
22+
end

app/helpers/oai_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module OaiHelper
2+
end

app/models/material.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
require 'rails/html/sanitizer'
2+
require 'json/ld'
3+
require 'rdf'
4+
require 'rdf/rdfxml'
5+
require 'builder'
26

37
class Material < ApplicationRecord
48
include PublicActivity::Common
@@ -186,4 +190,58 @@ def duplicate
186190
def archived?
187191
status == 'archived'
188192
end
193+
194+
def to_rdf
195+
jsonld_str = to_bioschemas[0].to_json
196+
197+
graph = RDF::Graph.new
198+
JSON::LD::Reader.new(jsonld_str) do |reader|
199+
reader.each_statement { |stmt| graph << stmt }
200+
end
201+
202+
rdfxml_str = graph.dump(:rdfxml, prefixes: { sdo: 'http://schema.org/', dc: 'http://purl.org/dc/terms/' })
203+
rdfxml_str.sub(/\A<\?xml.*?\?>\s*/, '') # remove XML declaration because this is used inside OAI-PMH response
204+
end
205+
206+
def to_oai_dc
207+
xml = ::Builder::XmlMarkup.new
208+
xml.tag!('oai_dc:dc',
209+
'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
210+
'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
211+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
212+
'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd') do
213+
xml.tag!('dc:title', title)
214+
xml.tag!('dc:description', description)
215+
authors.each { |a| xml.tag!('dc:creator', a) }
216+
contributors.each { |a| xml.tag!('dc:contributor', a) }
217+
xml.tag!('dc:publisher', content_provider.title) if content_provider
218+
219+
xml.tag!('dc:format', 'text/html')
220+
xml.tag!('dc:language', 'en')
221+
xml.tag!('dc:rights', licence) if licence.present?
222+
223+
[date_published, date_created, date_modified].compact.each do |d|
224+
xml.tag!('dc:date', d.iso8601)
225+
end
226+
227+
if doi.present?
228+
doi_iri = doi.start_with?('http://', 'https://') ? doi : "https://doi.org/#{doi}"
229+
xml.tag!('dc:identifier', doi_iri)
230+
else
231+
xml.tag!('dc:identifier', url)
232+
end
233+
234+
(keywords + scientific_topics.map(&:uri) + operations.map(&:uri)).each do |s|
235+
xml.tag!('dc:subject', s)
236+
end
237+
238+
xml.tag!('dc:type', 'http://purl.org/dc/dcmitype/Text')
239+
xml.tag!('dc:type', 'https://schema.org/LearningResource')
240+
resource_type.each { |t| xml.tag!('dc:type', t) }
241+
242+
xml.tag!('dc:relation', "#{TeSS::Config.base_url}#{Rails.application.routes.url_helpers.material_path(self)}")
243+
xml.tag!('dc:relation', content_provider.url) if content_provider&.url
244+
end
245+
xml.target!
246+
end
189247
end

app/views/static/home/_counters.html.erb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
<div id='resource_count' class='resource-counter-number'> <%= @count_strings[feature] %> </div>
1414
<div class='resource-counter-text'> <%= feature == 'events' ? 'Upcoming Events' : feature.titleize %> </div>
15-
1615
<% end %>
1716
</li>
1817
<% end %>

config/environment.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Load the Rails application.
2-
require_relative "application"
2+
require_relative 'application'
33

44
# Initialize the Rails application.
55
Rails.application.initialize!
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Configure OAI-PMH library
2+
# see comments in: https://github.com/code4lib/ruby-oai/blob/54ea6f7f5b1e2c1be5d0a7cc61cb696b5e653d8a/lib/oai/provider.rb#L98
3+
require 'oai'
4+
require 'uri'
5+
6+
class OAIRDF < OAI::Provider::Metadata::Format
7+
def initialize
8+
@prefix = 'rdf'
9+
@schema = 'http://www.openarchives.org/OAI/2.0/rdf.xsd'
10+
@namespace = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
11+
@element_namespace = 'rdf'
12+
end
13+
end
14+
15+
class TrainingProvider < OAI::Provider::Base
16+
repository_name TeSS::Config.site['title']
17+
repository_url "#{TeSS::Config.base_url}/oai-pmh"
18+
record_prefix "oai:#{URI(TeSS::Config.base_url).host}"
19+
admin_email TeSS::Config.contact_email
20+
sample_id '142' # so that example id is oai:domain:142
21+
22+
register_format(OAIRDF.instance)
23+
end
24+
25+
Rails.application.config.after_initialize do
26+
TrainingProvider.source_model OAI::Provider::ActiveRecordWrapper.new(Material.where(visible: true))
27+
rescue ActiveRecord::ActiveRecordError
28+
Rails.logger.debug 'There is no database yet or some other error, so the OAI-PMH endpoint is not configured.'
29+
end

config/routes.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@
177177

178178
get 'up' => 'health_check#show'
179179

180+
match 'oai-pmh', to: "oai#index", via: [:get, :post]
181+
180182
# The priority is based upon order of creation: first created -> highest priority.
181183
# See how all your routes lay out with "rake routes".
182184

0 commit comments

Comments
 (0)