Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hudmol/as_reftracker
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.0
Choose a base ref
...
head repository: hudmol/as_reftracker
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref

Commits on May 13, 2024

  1. Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Czaki Grzegorz Bokota
    Copy the full SHA
    1588e64 View commit details

Commits on Jun 14, 2024

  1. Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Czaki Grzegorz Bokota
    Copy the full SHA
    0e1426d View commit details
  2. Copy the full SHA
    d7e3ff2 View commit details

Commits on Jun 15, 2024

  1. Copy the full SHA
    d7c50c8 View commit details
  2. Copy the full SHA
    f3608df View commit details
  3. Copy the full SHA
    7174c03 View commit details
  4. Copy the full SHA
    7fcf0e0 View commit details

Commits on Jun 16, 2024

  1. better description handling

    jambun committed Jun 16, 2024
    Copy the full SHA
    bbad1dc View commit details
  2. Copy the full SHA
    4c70d22 View commit details

Commits on Jun 17, 2024

  1. Copy the full SHA
    2242f7c View commit details
  2. Copy the full SHA
    6b8db51 View commit details
  3. Copy the full SHA
    ca97764 View commit details

Commits on Jun 20, 2024

  1. Copy the full SHA
    bd41e5f View commit details

Commits on Jun 21, 2024

  1. Copy the full SHA
    0775b80 View commit details
  2. Copy the full SHA
    a55689e View commit details
  3. Updated README

    jambun committed Jun 21, 2024
    Copy the full SHA
    a16ef24 View commit details

Commits on Oct 10, 2024

  1. Copy the full SHA
    b4bfad1 View commit details

Commits on Oct 21, 2024

  1. Copy the full SHA
    8396e71 View commit details
  2. Copy the full SHA
    3a41b48 View commit details

Commits on Oct 23, 2024

  1. Copy the full SHA
    9e032ae View commit details

Commits on Nov 28, 2024

  1. Copy the full SHA
    5d7ca76 View commit details
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -6,13 +6,13 @@ records as Accessions.
----
Developed by Hudson Molonglo for the National Library of Australia.

© 2022 Hudson Molonglo Pty Ltd.
© 2022-2024 Hudson Molonglo Pty Ltd.

----

## Compatibility

This plugin was developed against ArchivesSpace v3.2. Although it has not
This plugin was developed against ArchivesSpace v3.3.1. Although it has not
been tested against other versions, it will probably work as expected on all
2.x and 3.x versions.

@@ -34,9 +34,10 @@ dropdown in the application toolbar (top right of page). You should see a
`RefTracker Offers` option.


## Import Offers
## Import Offers as Accessions

1. Click on `Plug-ins` > `RefTracker Offers`
2. Offers with a status of `Closed successful` are shown, most recently closed first
3. Click the checkboxes next to the offers you would like to import
2. Offers with identifiers that are not already in AS are shown in reverse Offer Number order
3. Offers can also be found individually by Offer Number
3. Click the checkboxes next to the Offers you would like to import
4. Click the `Import` button
34 changes: 26 additions & 8 deletions backend/controllers/reftracker.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
class ArchivesSpaceService < Sinatra::Base

Endpoint.get('/plugins/reftracker/offers')
.description("Get a list of offers that are ready to import from RefTracker")
.params(['page', Integer, "Page number", :default => 1])
.description("Get a list of offers that are ready to import from RefTracker, or a specific offer")
.params(['page', Integer, "Page number", :default => 1],
['ono', String, 'The offer number to get', :optional => true])
.permissions([])
.returns([200, "[offers]"]) \
do
RefTrackerClient.closed_offers(params[:page])
begin
json_response(RefTrackerClient.manuscript_offers(params[:page], params[:ono]))
rescue ReftrackerAPIException => e
json_response({:error => e.message})
end
end

Endpoint.get('/plugins/reftracker/offer/:ono')
.description("Get an offer from RefTracker")
.params(['ono', String, 'The offer number to get'])
.permissions([])
.returns([200, "offer"], [404, 'not found']) \
.returns([200, "offer"], [400, 'API error']) \
do
begin
json_response(RefTrackerClient.get_question(params[:ono]))
rescue RecordNotFound => e
json_response({:error => e.message}, 404)
rescue ReftrackerAPIException => e
json_response({:error => e.message}, 400)
end
end

Endpoint.get('/plugins/reftracker/codetable/:table')
.description("Get a RefTracker code table")
.params(['table', String, 'The code table to get'])
.permissions([])
.returns([200, "codetable"], [400, 'API error']) \
do
begin
RefTrackerClient.get_codetable(params[:table])
rescue ReftrackerAPIException => e
json_response({:error => e.message}, 400)
end
end

@@ -27,7 +45,7 @@ class ArchivesSpaceService < Sinatra::Base
.params(["repo_id", :repo_id],
['offer', String, 'The offer number to import'])
.permissions([:update_accession_record])
.returns([200, "success"], [404, "not found"]) \
.returns([200, "success"], [400, "API error"]) \
do
json_response(RefTrackerHandler.import(params[:offer]))
end
@@ -37,7 +55,7 @@ class ArchivesSpaceService < Sinatra::Base
.params(["repo_id", :repo_id],
['offers', String, 'The Offer numbers to import'])
.permissions([:update_accession_record])
.returns([200, "success"], [404, "not found"]) \
.returns([200, "success"], [400, "API error"]) \
do
json_response(RefTrackerHandler.import(params[:offers]))
end
124 changes: 95 additions & 29 deletions backend/model/reftracker_client.rb
Original file line number Diff line number Diff line change
@@ -10,53 +10,119 @@ class RefTrackerClient
end


def self.strip_markup(text)
Nokogiri::XML.fragment(Nokogiri::XML.fragment(text).text.gsub('&', '&amp;')).text
end


def self.get_question(question_no)
resp = ASUtils.json_parse(self.get('getQuestion', {:parameters => {:key => 'question_no', :value => question_no, :format => 'json'}.to_json}))

# if the question doesn't exist it returns this:
# [{"result":"No Question for these parameters format:json key:question_no value:blah","status":"200"}]
# so just going to assume if it is an array then it wasn't found - otherwise it would be a hash
if resp.is_a? Array
raise RecordNotFound.new("No Question for number #{question_no}")
raise ReftrackerAPIException.new("No offer for number #{question_no}")
end

# decode and strip markup - phewee - reftracker is not being friendly here
Hash[resp.map {|k, v| [k, Nokogiri::XML.fragment(Nokogiri::XML.fragment(v).text.gsub('&', '&amp;')).text]}]
Hash[resp.map {|k, v| [k, strip_markup(v)]}]
end


def self.manuscript_offers(page = 1, offer_number)
offers = []
if offer_number
resp = ASUtils.json_parse(self.get('getQuestion', {:parameters => {:key => 'question_no', :value => offer_number, :format => 'json'}.to_json}))

if resp.is_a? Array
raise ReftrackerAPIException.new("No offer for number #{offer_number}")
end

offers << resp
else
columns = [
'question_no',
'question_text',
'bib_udf_tb03',
'bib_title',
'client_name',
'question_format',
'question_update_datetime',
]

# status of 700 is 'Closed successful' found this using /codetable?table=status
# :status => '700' - not doing this any more

# qtype of 100 is 'Offerer service' - new requirement
# db = 5 is a magic number from the original plugin.
# without it the api complains about missing a param called 'source'
# sortby = 3 is ClosedDate -- no longer using this

# last update: qnudt - can't sort by this so qno instead
# :sortby => '50' is question_no

# question_format = Manuscripts
# :qnfmid => '10'

search_params = {
:qtype => '100',
:qnfmid => '10',
:db => '5',
:sortby => '50',
:sortorder => 'DESC',
:columnList => columns.join('|'),
:pagenumber => page,
:pagesize => 200,
}

offers = ASUtils.json_parse(self.get('search', {:parameters => search_params.to_json}))
end

# here's how accession.identifier looks in the db :(
# ["moo",null,null,null]
# NLA only uses id_0 so this works
offer_ids = offers.map{|offer| offer.fetch('bib_udf_tb03', '')}.select{|id| !id.empty?}.map{|id| '["' + id + '",null,null,null]'}.compact

# find out which of the offer_ids already exist in AS
found_ids = nil
DB.open{ |db| found_ids = db[:accession].filter(:identifier => offer_ids).select(:identifier).map{|i| ASUtils.json_parse(i[:identifier]).first}}

# then filter out the found ids, and any offers without an id
offers.select{|offer| !offer.fetch('bib_udf_tb03', '').empty? && !found_ids.include?(offer['bib_udf_tb03'])}
.map do |offer|
offer['question_text_stripped'] = strip_markup(offer['question_text'])
if offer['question_text_stripped'].length > 400
offer['short_description'] = offer['question_text_stripped'][0..400]
end
offer
end
end


def self.closed_offers(page = 1)
columns = [
'question_no',
'question_text',
'bib_udf_tb03',
'bib_title',
'client_name',
'question_closed_datetime',
]

# status of 700 is 'Closed successful' found this using /codetable?table=status
# qtype of 100 is 'Offerer service' - new requirement
# db = 5 is a magic number from the original plugin.
# without it the api complains about missing a param called 'source'
# sortby = 3 is ClosedDate
# ok now got an endpoint for codetables:
# format is format so:
# /plugins/reftracker/codetable/format
# tells me that format for Manuscripts is 10
def self.get_codetable(table)
search_params = {
:status => '700',
:qtype => '100',
:db => '5',
:sortby => '3',
:sortorder => 'DESC',
:columnList => columns.join('|'),
:pagenumber => page,
:pagesize => 20,
:codetable => table,
}
self.get('search', {:parameters => search_params.to_json})
self.get('codetable', {:parameters => search_params.to_json})
end


def self.get(uri, params = {})
url = URI(File.join(AppConfig[:reftracker_base_url], uri))
url.query = URI.encode_www_form(params) unless params.empty?
Net::HTTP.get(url)
begin
url = URI(File.join(AppConfig[:reftracker_base_url], uri))
url.query = URI.encode_www_form(params) unless params.empty?
Net::HTTP.get(url)
rescue => e
if [SocketError, IOError].include?(e.class)
raise ReftrackerAPIException.new('Failed to connect to RefTracker')
else
raise ReftrackerAPIException.new(e.message)
end
end
end
end
34 changes: 6 additions & 28 deletions backend/model/reftracker_mapper.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'date'

class RefTrackerMapper

include JSONModel
@@ -6,31 +8,9 @@ class RefTrackerMapper
def self.map_events(qp, accession_uri, agent_uri)
events = []

if qp['question_udf_dt01']
events << {
'event_type' => 'agreement_sent',
'date' => {
'date_type' => 'single',
'label' => 'event',
'begin' => qp['question_udf_dt01'].split[0],
},
'linked_records' => [{'ref' => accession_uri, 'role' => 'source'}],
'linked_agents' => [{'ref' => agent_uri, 'role' => 'transmitter'}],
}
end

if qp['question_udf_dt02']
events << {
'event_type' => 'agreement_signed',
'date' => {
'date_type' => 'single',
'label' => 'event',
'begin' => qp['question_udf_dt02'].split[0],
},
'linked_records' => [{'ref' => accession_uri, 'role' => 'source'}],
'linked_agents' => [{'ref' => agent_uri, 'role' => 'transmitter'}],
}
end
# NLA no longer requires events
# The accession_events plugin is used to create events for all new accessions
# Leaving this here as a reminder and a placeholder in case events are needed again

events
end
@@ -139,7 +119,7 @@ def self.map_accession(qp, agent_uri, subject_uris)

acc['id_0'] = qp['bib_udf_tb03']

acc['accession_date'] = qp['question_closed_datetime'].split[0]
acc['accession_date'] = Date.today.iso8601

acc['acquisition_type'] = qp['question_udf_cl03'].downcase
acc['content_description'] = qp['question_udf_ta08'] || qp['question_text']
@@ -150,8 +130,6 @@ def self.map_accession(qp, agent_uri, subject_uris)
acc['user_defined']['boolean_1'] = qp['bib_udf_cl01'] == 'New collection'
acc['user_defined']['integer_2'] = qp['bib_callno']

acc['user_defined']['real_3'] = qp['bib_price_actual'].gsub(/,/, '') if qp['bib_price_actual']

acc['user_defined']['string_2'] = qp['question_no']
acc['user_defined']['string_3'] = qp['question_udf_tb08']
acc['user_defined']['text_2'] = qp['bib_udf_ta02']
8 changes: 8 additions & 0 deletions frontend/assets/reftracker_offers.css
Original file line number Diff line number Diff line change
@@ -3,6 +3,14 @@
white-space: nowrap;
}

.rto-title {
min-width: 200px;
}

.rto-td {
vertical-align: middle !important;
}

.rto-full-description-pane {
display: none;
}
10 changes: 9 additions & 1 deletion frontend/controllers/reftracker_offers_controller.rb
Original file line number Diff line number Diff line change
@@ -3,7 +3,15 @@ class ReftrackerOffersController < ApplicationController
set_access_control "update_accession_record" => [:index, :import]

def index
render :locals => { :offers => JSONModel::HTTP::get_json('/plugins/reftracker/offers', :page => params['page']) }
offers = JSONModel::HTTP::get_json('/plugins/reftracker/offers', :page => (params['page'] || 1), :ono => params['offer_number'])
error = nil
if offers.is_a?(Hash) && offers['error']
error = offers['error']
offers = []
end
render :locals => { :offers => offers,
:offer_number => params['offer_number'],
:error => error }
end

def import
10 changes: 8 additions & 2 deletions frontend/locales/en.yml
Original file line number Diff line number Diff line change
@@ -3,12 +3,18 @@ en:
reftracker_offers:
label: RefTracker Offers
import_button_label: Import
find_button_label: Find Offer
cancel_find_button_label: Cancel
results:
closed_offers: Showing closed offers, most recent first. Select offers using the checkboxes and click Import to import them as Accessions.
results_current_at: "RefTracker results current at: <b>%{time}</b>"
offers_description: Showing <b>Manuscripts</b> offers <b>with an identifier</b> that is <b>not already in ArchivesSpace</b>, in descending offer number order.
offers_error_description: Problem retrieving offers
found_offer_description: Showing offer <b>%{offer_number}</b>. Click Cancel to see all offers.
offer_error_description: Problem finding offer <b>%{offer_number}</b>
offers_instruction: Select offers using the checkboxes and click Import to import them as Accessions.
question_no: "Offer #"
bib_udf_tb03: Identifier
client_name: Offerer
bib_title: Collection title
question_text: Description
question_closed_datetime: Closed on
import: Import
160 changes: 74 additions & 86 deletions frontend/views/reftracker_offers/index.html.erb
Original file line number Diff line number Diff line change
@@ -3,104 +3,87 @@

<%= render_aspace_partial :partial => "shared/form_messages" %>

<p><%= I18n.t('plugins.reftracker_offers.results.closed_offers') %></p>

<%= form_tag({:controller => :reftracker_offers, :action => :import}) do %>
<% if offer_number %>
<% if error %>
<p class="alert alert-danger"><%= I18n.t('plugins.reftracker_offers.results.offer_error_description', :offer_number => offer_number) %>: <%= error %></p>
<% else %>
<p class="alert alert-info"><%= I18n.t('plugins.reftracker_offers.results.found_offer_description', :offer_number => offer_number) %></p>
<% end %>
<% else %>
<% if error %>
<p class="alert alert-danger"><%= I18n.t('plugins.reftracker_offers.results.offers_error_description') %>: <%= error %></p>
<% else %>
<p class="alert alert-info"><%= I18n.t('plugins.reftracker_offers.results.offers_description') %></p>
<% end %>
<% end %>

<button type="submit" disabled="true" id="rto-submit" class="btn btn-sm btn-primary"><%= I18n.t("plugins.reftracker_offers.import_button_label") %></button>
<%= form_tag({:controller => :reftracker_offers, :action => :index}, :method => :get) do %>

<br/><br/>
<input type="text" name="offer_number" placeholder="Enter an offer number" value="<%= offer_number %>">
<button type="submit" id="rto-offer-submit" class="btn btn-sm btn-primary"><%= I18n.t("plugins.reftracker_offers.find_button_label") %></button>

<% if page = offers.first %>
<% current_page = page['search_page'].to_i %>
<% last_page = (page['search_total'].to_f / page['search_page_size'].to_f).ceil %>
<p>
Showing page <strong><%= current_page %></strong> of <strong><%= last_page %></strong>
</p>
<% if offer_number %>
<a type="cancel" id="rto-offer-cancel" href="<%= url_for(:controller => :reftracker_offers, :action => :index) %>" class="btn btn-sm btn-default"><%= I18n.t("plugins.reftracker_offers.cancel_find_button_label") %></a>
<% end %>
<% end %>

<table class="table table-striped table-bordered table-condensed">
<hr/>

<tr>
<th></th>
<th><%= I18n.t('plugins.reftracker_offers.results.question_no') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.client_name') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.bib_udf_tb03') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.bib_title') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.question_text') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.question_closed_datetime') %></th>
</tr>
<p><%= I18n.t('plugins.reftracker_offers.results.results_current_at', :time => Time.now.rfc2822) %></p>

<% offers.each do |offer| %>
<tr>
<td class="rto-td">
<input type="checkbox" name="offer_<%= offer['question_no'] %>" onchange="reftrackerCheckboxClick();"/>
</td>
<td class="rto-td rto-nowrap">
<%= offer['question_no'] %>
</td>
<td class="rto-td rto-nowrap">
<%= offer['client_name'] %>
</td>
<td class="rto-td rto-nowrap">
<%= offer['bib_udf_tb03'] %>
</td>
<td class="rto-td">
<%= offer['bib_title'] %>
</td>
<td class="rto-td">
<%= offer['question_text'] %>
</td>
<td class="rto-td rto-nowrap">
<%= offer['question_closed_datetime'].split[0] %>
</td>
</tr>
<% end %>
<p><%= I18n.t('plugins.reftracker_offers.results.offers_instruction') %></p>

</table>
<hr/>

<% end %>
<%= form_tag({:controller => :reftracker_offers, :action => :import}) do %>

<% if page = offers.first %>
<% current_page = page['search_page'].to_i %>
<% last_page = (page['search_total'].to_f / page['search_page_size'].to_f).ceil %>
<% if last_page > 1 %>

<div class="pagination-centered">
<ul class="pagination pagination-sm">
<% if current_page > 1 %>
<li>
<%= link_to I18n.t("pagination.previous").html_safe, {:controller => :reftracker_offers, :action => :index, :page => current_page - 1} %>
</li>
<% else %>
<li class="disabled"><a href="#"><%= I18n.t("pagination.previous").html_safe %></a></li>
<% end %>

<% if last_page > 1 %>
<% (1..last_page).each do |p| %>
<% if p == current_page %>
<li class="disabled"><a href="#"><%= p %></a></li>
<% else %>
<li>
<%= link_to p, {:controller => :reftracker_offers, :action => :index, :page => p} %>
</li>
<% end %>
<% end %>
<% end %>

<% if current_page < last_page %>
<li>
<%= link_to I18n.t("pagination.next").html_safe, {:controller => :reftracker_offers, :action => :index, :page => current_page + 1} %>
</li>
<% else %>
<li class="disabled"><a href="#"><%= I18n.t("pagination.next").html_safe %></a></li>
<% end %>
</ul>
</div>
<% end %>
<% end %>
<button type="submit" disabled="true" id="rto-submit" class="btn btn-sm btn-primary"><%= I18n.t("plugins.reftracker_offers.import_button_label") %></button>

<hr/>

<table class="table table-striped table-bordered table-condensed">
<tr>
<th></th>
<th><%= I18n.t('plugins.reftracker_offers.results.question_no') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.client_name') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.bib_udf_tb03') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.bib_title') %></th>
<th><%= I18n.t('plugins.reftracker_offers.results.question_text') %></th>
</tr>

<% offers.each do |offer| %>
<tr>
<td class="rto-td">
<input type="checkbox" name="offer_<%= offer['question_no'] %>" onchange="reftrackerCheckboxClick();"/>
</td>
<td class="rto-td rto-nowrap">
<%= offer['question_no'] %>
</td>
<td class="rto-td rto-nowrap">
<%= offer['client_name'].html_safe %>
</td>
<td class="rto-td rto-nowrap">
<%= offer['bib_udf_tb03'] %>
</td>
<td class="rto-td rto-title">
<%= offer['bib_title'].html_safe %>
</td>
<td class="rto-td">
<% if offer['short_description'] %>
<%= offer['short_description'].html_safe %>
...
<a class="pull-right btn btn-sm btn-default rto-more-btn" onclick="reftrackerMoreButtonClick(this);">View full description</a>
<div data-offer="<%= offer['question_no'] %>" class="rto-full-description-pane">
<%= offer['question_text'].html_safe %>
</div>
<% else %>
<%= offer['question_text_stripped'].html_safe %>
<% end %>
</td>
</tr>
<% end %>
</table>
<% end %>

<script>
$('#rto-submit').prop('disabled', true);
@@ -113,5 +96,10 @@
$('#rto-submit').prop('disabled', true);
}
};

var reftrackerMoreButtonClick = function(btn) {
var fullDesc = btn.nextElementSibling;
AS.openQuickModal(fullDesc.dataset.offer + ' full description', fullDesc.innerText);
};
</script>