Skip to content

Commit

Permalink
Merge pull request #1497 from seek4science/cff-citation
Browse files Browse the repository at this point in the history
Use CFF to generate citation if present
  • Loading branch information
fbacall authored Jul 3, 2023
2 parents f8627f4 + ee9df1b commit e9175bb
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 20 deletions.
4 changes: 2 additions & 2 deletions app/assets/javascripts/application_shared.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,9 @@ $j(document).ready(function () {
var style = $j(this).val();
var url = $j(this).data('url');
var target = $j('#citation');
var currentStyle = $j('#citation').data('citationStyle');
var currentStyle = $j('#citation div').data('citationStyle');

if (style && style !== currentStyle) {
if (style && currentStyle && style !== currentStyle) {
$j.ajax({
url: url,
data: { style: style },
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/citations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class CitationsController < ApplicationController

def fetch
respond_to do |format|
format.js { render partial: 'assets/citation', locals: { doi: params[:doi], style: params[:style] } }
format.js { render partial: 'assets/citation_from_doi', locals: { doi: params[:doi], style: params[:style] } }
end
end

Expand Down
14 changes: 9 additions & 5 deletions app/controllers/concerns/raw_display.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
module RawDisplay
extend ActiveSupport::Concern

RAW_DISPLAY_FORMATS = %w(notebook markdown pdf text image)
RAW_DISPLAY_FORMATS = %w(notebook markdown pdf text image citation)

# A method for rendering a given Git/Content Blob in an HTML "Viewer"
def render_display(blob, url_options: {})
if params[:display]
renderer = Seek::Renderers.const_get("#{params[:display].classify}Renderer").new(blob, url_options: url_options)
else
renderer = Seek::Renderers::RendererFactory.instance.renderer(blob, url_options: url_options)
renderer = Seek::Renderers.const_get("#{params[:display].classify}Renderer").new(blob, url_options: url_options, params: params)
else # Render default display
renderer = Seek::Renderers::RendererFactory.instance.renderer(blob, url_options: url_options, params: params)
end
if renderer.can_render?
# check if allowed by cookies
unless renderer.external_embed? && !cookie_consent.allow_embedding?
response.set_header('Content-Security-Policy', renderer.content_security_policy)
render renderer.format => renderer.render_standalone.html_safe, layout: renderer.layout
if params[:disposition] == 'inline' # Partial view
render renderer.format => renderer.render.html_safe
else # Full page view
render renderer.format => renderer.render_standalone.html_safe, layout: renderer.layout
end
end
else
raise ActionController::UnknownFormat
Expand Down
10 changes: 8 additions & 2 deletions app/helpers/citations_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
require 'csl/styles'

module CitationsHelper
def render_citation(doi, style)
Seek::Citations.generate(doi, style)
def render_doi_citation(doi, style)
Seek::Citations.from_doi(doi, style)
rescue JSON::ParserError, RestClient::Exception
'An error occurred whilst fetching the citation'
end

def render_cff_citation(blob, style)
Seek::Citations.from_cff(blob, style)
rescue StandardError
'An error occurred whilst fetching the citation'
end

def citation_style_options(selected = nil)
selected ||= selected_citation_style
options_for_select(Seek::Citations.style_pairs, selected)
Expand Down
10 changes: 8 additions & 2 deletions app/views/assets/_citation_box.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<% snapshot ||= nil %>
<% blob ||= nil %>
<% url = blob ? blob.content_path(display: 'citation', disposition: 'inline') : citation_path(doi) %>

<div class="panel panel-default">
<div class="panel-heading">
Expand All @@ -10,11 +12,15 @@
</div>
<div class="panel-body">
<div id="citation" class="well well-sm citation-box">
<%= render partial: 'assets/citation', locals: { doi: doi, style: selected_citation_style } %>
<% if blob %>
<%= render partial: 'assets/citation_from_cff', locals: { blob: blob, style: selected_citation_style } %>
<% else %>
<%= render partial: 'assets/citation_from_doi', locals: { doi: doi, style: selected_citation_style } %>
<% end %>
</div>

<%= select_tag(nil, citation_style_options, id: 'citation-style-select', class: 'form-control',
data: { url: citation_path(doi) }) %>
data: { url: url }) %>
<% if snapshot %>
<span class="subtle">
Expand Down
3 changes: 3 additions & 0 deletions app/views/assets/_citation_from_cff.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= content_tag(:div, data: { 'citation-style' => style }) do %>
<%= render_cff_citation(blob, style) %>
<% end %>
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<%= content_tag(:div, data: { 'citation-style' => style }) do %>
<%= render_citation(doi, style) %>
<%= render_doi_citation(doi, style) %>
<% end %>
2 changes: 2 additions & 0 deletions app/views/assets/_resource_main_content_right.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
<% elsif versioned_resource.respond_to?(:doi) %>
<% if versioned_resource.doi.present? %>
<%= render :partial => "assets/citation_box", locals: { doi: versioned_resource.doi } %>
<% elsif versioned_resource.is_git_versioned? && versioned_resource.file_exists?(Seek::WorkflowExtractors::CFF::FILENAME) %>
<%= render :partial => "assets/citation_box", locals: { blob: versioned_resource.get_blob(Seek::WorkflowExtractors::CFF::FILENAME) } %>
<% elsif resource.can_manage? && Seek::Config.doi_minting_enabled && resource.supports_doi? %>
<%= render :partial => "assets/citation_instructions", locals: { resource: resource, versioned_resource: versioned_resource } %>
<% end %>
Expand Down
21 changes: 18 additions & 3 deletions lib/seek/citations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ module Seek
class Citations
DEFAULT = 'apa' # This could be a setting one day

def self.generate(doi, style)
def self.from_doi(doi, style)
generate(doi_to_csl(doi), style)
end

def self.from_cff(blob, style)
generate(cff_to_csl(blob), style)
end

def self.generate(csl, style)
cp = CiteProc::Processor.new(style: style, format: 'html')
cp.register(csl(doi).merge(id: :_))
cp.register(csl.merge(id: :_))
cp.render(:bibliography, id: :_).last.html_safe
end

Expand All @@ -18,13 +26,20 @@ def self.style_pairs
end
end

def self.csl(doi)
def self.doi_to_csl(doi)
Rails.cache.fetch("citation-#{doi}") do
resp = RestClient.get("https://doi.org/#{Addressable::URI.escape(doi)}", accept: 'application/vnd.citationstyles.csl+json')
JSON.parse(resp)
end
end

def self.cff_to_csl(blob)
Rails.cache.fetch("citation-cff-#{blob.cache_key}") do
cff = ::CFF::File.read(blob.file)
BibTeX.parse(cff.to_bibtex).to_citeproc.first
end
end

def self.generate_style_pairs
CSL::Style.list.map { |key| [CSL::Style.load(key).title, key] }.sort_by { |s| s[0] }
end
Expand Down
4 changes: 4 additions & 0 deletions lib/seek/content_type_detection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ def is_image?(blob = self)
blob.content_type.try(:split, '/').try(:first) == 'image'
end

def is_cff?(blob = self)
blob.content_type_file_extensions.include?('cff')
end

def is_image_convertable?(blob = self)
(IMAGE_CONVERTABLE_FORMAT & blob.content_type_file_extensions).any?
end
Expand Down
12 changes: 10 additions & 2 deletions lib/seek/renderers/blob_renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ module Renderers
class BlobRenderer
include ActionView::Helpers

attr_reader :blob
attr_reader :blob, :params

def initialize(git_blob_or_blob, url_options: {})
def initialize(git_blob_or_blob, url_options: {}, params: {})
@blob = git_blob_or_blob
@url_options = url_options
@params = params
end

def render
Expand Down Expand Up @@ -66,6 +67,13 @@ def render_template(template_path, variables = {}, layout: 'application')
layout: layout
)
end

def render_partial(partial_path, variables = {})
ApplicationController.renderer.render(
partial: partial_path,
locals: variables
)
end
end
end
end
19 changes: 19 additions & 0 deletions lib/seek/renderers/citation_renderer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Seek
module Renderers
class CitationRenderer < BlobRenderer
def can_render?
blob.is_cff?
end

def render_content
render_partial('assets/citation_from_cff', blob: blob, style: style)
end

private

def style
params[:style] || Seek::Citations::DEFAULT
end
end
end
end
4 changes: 2 additions & 2 deletions lib/seek/renderers/renderer_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ module Renderers
class RendererFactory
include Singleton

def renderer(blob, url_options: {})
def renderer(blob, url_options: {}, params: {})
renderer_class = cache.fetch(blob.cache_key) { detect_renderer(blob).name }.constantize

renderer_class.new(blob, url_options: url_options)
renderer_class.new(blob, url_options: url_options, params: params)
end

private
Expand Down
58 changes: 58 additions & 0 deletions test/functional/git_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,20 @@ def setup
assert_select '.markdown-body h1', text: 'FAIRDOM-SEEK'
end

test 'should display blob as markdown inline' do
@git_version.add_file('file.md', FactoryBot.create(:markdown_content_blob))
disable_authorization_checks { @git_version.save! }

get :raw, xhr: true, params: { workflow_id: @workflow.id, version: @git_version.version, path: 'file.md',
display: 'markdown', disposition: 'inline' }

assert_response :success
assert @response.header['Content-Type'].start_with?('text/html')
assert_equal ApplicationController::USER_CONTENT_CSP, @response.header['Content-Security-Policy']
assert_select 'body', count: 0
assert_select 'iframe'
end

test 'should display blob as jupyter' do
@git_version.add_file('file.ipynb', FactoryBot.create(:jupyter_notebook_content_blob))
disable_authorization_checks { @git_version.save! }
Expand Down Expand Up @@ -784,4 +798,48 @@ def setup
assert_equal 'file.md', log.data[:path]
assert_equal 'markdown', log.data[:display]
end

test 'should display CFF blob as citation' do
@git_version.add_file('CITATION.cff', open_fixture_file('CITATION.cff'))
disable_authorization_checks { @git_version.save! }

get :raw, params: { workflow_id: @workflow.id, version: @git_version.version, path: 'CITATION.cff',
display: 'citation' }

assert_response :success
assert @response.header['Content-Type'].start_with?('text/html')
assert_equal ApplicationController::USER_CONTENT_CSP, @response.header['Content-Security-Policy']
assert_select 'body'
assert_select '#navbar', count: 0
assert_select 'div[data-citation-style=?]', 'apa', text: /van der Real Person, O\. T\./
end

test 'should display CFF blob as citation with selected style' do
@git_version.add_file('CITATION.cff', open_fixture_file('CITATION.cff'))
disable_authorization_checks { @git_version.save! }

get :raw, params: { workflow_id: @workflow.id, version: @git_version.version, path: 'CITATION.cff',
display: 'citation', style: 'bibtex' }

assert_response :success
assert @response.header['Content-Type'].start_with?('text/html')
assert_equal ApplicationController::USER_CONTENT_CSP, @response.header['Content-Security-Policy']
assert_select 'body'
assert_select '#navbar', count: 0
assert_select 'div[data-citation-style=?]', 'bibtex', text: /author=\{Real Person, One Truly van der, IV and/
end

test 'should display CFF blob as citation inline' do
@git_version.add_file('CITATION.cff', open_fixture_file('CITATION.cff'))
disable_authorization_checks { @git_version.save! }

get :raw, xhr: true, params: { workflow_id: @workflow.id, version: @git_version.version, path: 'CITATION.cff',
display: 'citation', disposition: 'inline', style: 'the-lancet' }

assert_response :success
assert @response.header['Content-Type'].start_with?('text/html')
assert_equal ApplicationController::USER_CONTENT_CSP, @response.header['Content-Security-Policy']
assert_select 'body', count: 0
assert_select 'div[data-citation-style=?]', 'the-lancet', text: /Real Person OT van der IV/
end
end
18 changes: 18 additions & 0 deletions test/functional/workflows_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1737,4 +1737,22 @@ def bad_generator.write_graph(struct)
# Reset the view parameter
session.delete(:view)
end

test 'can get citation for workflow with CFF' do
workflow = FactoryBot.create(:local_git_workflow, policy: FactoryBot.create(:public_policy))

get :show, params: { id: workflow }
assert_response :success
assert_select '#citation', text: /van der Real Person, O\. T\./, count: 0

gv = workflow.latest_git_version
disable_authorization_checks do
gv.add_file('CITATION.cff', open_fixture_file('CITATION.cff'))
disable_authorization_checks { gv.save! }
end

get :show, params: { id: workflow }
assert_response :success
assert_select '#citation', text: /van der Real Person, O\. T\./, count: 1
end
end

0 comments on commit e9175bb

Please sign in to comment.