Skip to content

Commit

Permalink
Merge pull request #3 from dartmouth-dltg/as-271
Browse files Browse the repository at this point in the history
Support ArchivesSpace 2.7.1
  • Loading branch information
jdshaw authored Jun 29, 2021
2 parents 6e38c68 + 6faf967 commit fa53048
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 129 deletions.
42 changes: 35 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Container Management Label Addon
Container Management Label Plugin
================================

ArchivesSpace plugin to add the ability to print labels within the browser to ArchivesSpace.
Expand Down Expand Up @@ -30,10 +30,10 @@ including an entry such as:

The labels that are available to print from within the browser are defined in two (2) plugin files and the config.rb file.

#### config.rb file
### config.rb file

Define the fields and order in which they should appear on the label. The fields must be a member of the following list unless
additional fields are set in the model (backend/model/label_data.rb)
additional fields are set in the model (`backend/model/label_data.rb`)

institution_name
repository_name
Expand All @@ -42,7 +42,9 @@ additional fields are set in the model (backend/model/label_data.rb)
agent_name
type
indicator
barcode
location
area
location_barcode

Each key should also indicate whether the field will be a default ("checked" => true) and whether the end user
Expand Down Expand Up @@ -86,7 +88,7 @@ config file does not contain the :container_management_labels key.
]


##### Label sizes for container_management_labels plugin.
#### Label sizes for container_management_labels plugin.
Label keys should match those used in the en.yml file in the plugin and should define a page size and margin.

AppConfig[:container_management_labels_pagesize] = {
Expand All @@ -101,7 +103,7 @@ Label keys should match those used in the en.yml file in the plugin and should d
If no label sizes are defined, the plugin will default to a letter size with 0.25in margins (defined in plugin_init.rb).
The keys should be named the same as in the CSS and the translation yml (below).

##### Autoscaling can also be turned on or off from the config file.
#### Autoscaling can also be turned on or off from the config file.
Autoscaling attempts to scale any label that overflows the defined label area by applying a css transform.
If "disabled" is set to false, an end user can turn autoscaling on or off on a per use basis.

Expand All @@ -110,7 +112,30 @@ If "disabled" is set to false, an end user can turn autoscaling on or off on a p
"disabled" => false
}

#### /frontend/assets/container_labels.css
#### Printing Files (and other sub container labels) can also be turned on or off from the config file.
Allows the user to print files (or other subcontainer labels). This will print labels for all
subcontainers associated with the top containers selected or may be filtered to only
archival objects whose level is set by the list below.
If "disabled" is set to false, an end user can turn printing files on or off on a per use basis.

AppConfig[:container_management_labels_print_files] = {
"checked" => true,
"disabled" => false
}

#### Printing Files (and other sub container labels) can also be set to only print specific levels.
Set this array to include the value of the archival object level that you want to print subcontainer
labels for. Acceptable values are
```
["class", "collection", "file", "fonds", "item",
"otherlevel", "recordgrp", "series", "subfonds",
"subgrp", "subseries"]
```
To set it to print file level labels only:

AppConfig[:container_management_labels_print_levels] = ["file"]

### /frontend/assets/container_labels.css

This is where the CSS is defined for the label fields and for the specific layouts. Note the convention
of using the label name as a namespace, eg ".dymo-30256". Also note that specific field css must use the same namespace as
Expand All @@ -128,7 +153,9 @@ The translations for each label are defined here. Note the hyphen in the label n

## Fields/Data Displayed

The labels will display the following fields (if data is present):
The labels will display the following fields
(if data is present and the fields are set to display in
`AppConfig['container_management_labels']`):

Insititution Name
Repository name
Expand All @@ -139,6 +166,7 @@ The labels will display the following fields (if data is present):
Top Container Indicator
Top Container Barcode
Location Title
Location Area
Location Barcode

## Barcodes
Expand Down
14 changes: 14 additions & 0 deletions backend/controllers/top_containers_label.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ class ArchivesSpaceService < Sinatra::Base
end

end

class ArchivesSpaceService < Sinatra::Base
Endpoint.post('/repositories/:repo_id/top_containers_labels/print_sub_labels')
.description("Bulk label data")
.params(["record_uris", [String], "A list of container uris"],
["repo_id", :repo_id])
.permissions([])
.returns([200, "Container data for label printing"]) \
do
label_data = LabelData.new(params[:record_uris])
json_response(label_data.sub_labels)
end

end
163 changes: 102 additions & 61 deletions backend/model/label_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,30 @@ class LabelData

include JSONModel

attr_accessor :labels
attr_accessor :labels, :sub_labels

def initialize(uris)
@uris = uris
@labels = build_label_data_short
@labels = build_label_data
@sub_labels = build_subcontainer_data
end

def build_label_data_short
def build_label_data

ids = @uris.map {|uri| JSONModel(:top_container).id_for(uri)}

# Eagerly load all of the Top Containers we'll be working with
load_top_containers(ids)

# Pre-store the links between our top containers and the archival objects they link to
top_container_to_ao_links = calculate_top_container_linkages(ids)

load_archival_objects(top_container_to_ao_links.values.flatten.uniq)

labels = []

# create a label for each top container
ids.each do |top_container_id|
tc = fetch_top_container(top_container_id)
ao = (top_container_to_ao_links[top_container_id].nil? || top_container_to_ao_links[top_container_id].first.nil?) ? nil : fetch_archival_object(top_container_to_ao_links[top_container_id].first)
agent = agent_for_top_container(tc)
area, location, location_barcode = location_for_top_container(tc)
resource_id, resource_title = resource_for_top_container(tc)
catalog_location = catalog_location_for_top_container(tc)
institution, repository = institution_repo_for_top_container(tc)
rm_id = ao.nil? ? nil : find_rm_id(ao['external_ids'])

labels << tc.merge({
"agent_name" => agent,
Expand All @@ -44,15 +37,84 @@ def build_label_data_short
"resource_title" => resource_title,
"institution_name" => institution,
"repository_name" => repository,
"records_management_id" => rm_id,
"catalog_location" => catalog_location
})
end

labels
return labels
end



def build_subcontainer_data

ids = @uris.map {|uri| JSONModel(:top_container).id_for(uri)}

# Eagerly load all of the Top Containers we'll be working with
load_top_containers(ids)

# Pre-store the links between our top containers and the archival objects they link to
top_container_to_ao_links = calculate_top_container_linkages(ids)

subcontainer_labels = []

# create a label for each top container
ids.each do |top_container_id|
tc = fetch_top_container(top_container_id)
agent = agent_for_top_container(tc)
area, location, location_barcode = location_for_top_container(tc)
resource_id, resource_title = resource_for_top_container(tc)
institution, repository = institution_repo_for_top_container(tc)

# if there's an indicator2, then its a sub_container like a file so add it to the sub_container labels
top_container_to_ao_links[top_container_id].each do |ao|

# skip this ao if it is not in the list of levels to print
if AppConfig[:container_management_labels_print_levels] && AppConfig[:container_management_labels_print_levels].count > 0
next unless AppConfig[:container_management_labels_print_levels].include? (ao["level"])
end

# replace the tc indicator with a concatted string from the subcontainer
sc_ind = []
sc_ind.push(tc["type"].capitalize + " " + tc["indicator"])

# if there is no type2, replace it with the title
# this will cover AOs without child indicators
# otherwise, if there is a type2, then concat that with indicator2
# same for type3 and indicator3
# we should end up with an array that looks like: ["Box 1", "File 2", Item 3"] or ["{FILE_TITLE}"]

if ao["type2"].nil?
ao["type2"] = ao["title"]
else
unless ao["indicator2"].nil?
sc_ind.push(ao["type2"].capitalize + " " + ao["indicator2"])
end
if !ao["type3"].nil? && !ao["indicator3"].nil?
sc_ind.push(ao["type3"].capitalize + " " + ao["indicator3"])
end
end

subcontainer_labels << tc.merge({
"sc_indicator" => sc_ind.compact.join(", "),
"agent_name" => agent,
"area" => area,
"location" => location,
"location_barcode" => location_barcode,
"resource_id" => resource_id,
"resource_title" => resource_title,
"institution_name" => institution,
"repository_name" => repository,
})
end
end

# replace the indicator with the subcontainer indicator
# do this for convenience so we don't need to add another key to the config
subcontainer_labels.each do |sl|
sl["indicator"] = sl["sc_indicator"]
end

return subcontainer_labels
end

private

# returns an agent name if a creator exists for the colelction linked to the top container
Expand All @@ -72,17 +134,6 @@ def agent_for_top_container(tc)
agent_name
end

# returns the catalog location for a top container
def catalog_location_for_top_container(tc)
tc_cat_loc = tc['collection'][0]['_resolved']['user_defined'] && tc['collection'][0]['_resolved']['user_defined']['enum_1'] ? tc['collection'][0]['_resolved']['user_defined']['enum_1'] : ''

if tc_cat_loc == 'novalue'
tc_cat_loc = ''
end

tc_cat_loc
end

# returns a location and location barcode for a top container
def location_for_top_container(tc)

Expand Down Expand Up @@ -118,55 +169,45 @@ def institution_repo_for_top_container(tc)
return institution, repository
end

def find_rm_id(ext_ids)
rms_source = AppConfig[:container_management_rms_source]
rm = ext_ids.select{|e| e['source'] == rms_source}.first
# Remove the leading "box_" from the RM ID as we don't need to display that
rm_id = rm.nil? ? nil : rm['external_id'].sub(/^box_/,'')
def load_top_containers(ids)
top_container_list = TopContainer.filter(:id => ids).all
top_container_json_records = Hash[TopContainer.sequel_to_jsonmodel(top_container_list).map {|tc| [tc.id, tc.to_hash(:trusted)]}]

@top_container_json_records = URIResolver.resolve_references(top_container_json_records, ['container_locations','repository','collection'])
end

rm_id
def fetch_top_container(id)
@top_container_json_records.fetch(id)
end

# Returns a hash like {123 => 456}, meaning "Top Container 123 links to Archival Object 456"
# Only includes the links to Archival Object box records
# Returns a hash like {123 => {"ao_di" => 456, "level" => File, ...}, ...}, meaning "Top Container 123 links to Archival Object 456 with level_id 789, etc"
def calculate_top_container_linkages(ids)
result = {}

TopContainer.linked_instance_ds.
join(:archival_object, :id => :instance__archival_object_id).
filter(:top_container__id => ids).
filter(:archival_object__other_level => 'box').
select(Sequel.as(:archival_object__id, :ao_id),
Sequel.as(:archival_object__level_id, :level),
Sequel.as(:archival_object__title, :ao_title),
Sequel.as(:sub_container__type_2_id, :type2),
Sequel.as(:sub_container__indicator_2, :indicator2),
Sequel.as(:sub_container__type_3_id, :type3),
Sequel.as(:sub_container__indicator_3, :indicator3),
Sequel.as(:top_container__id, :top_container_id)).each do |row|

result[row[:top_container_id]] ||= []
result[row[:top_container_id]] << row[:ao_id]
result[row[:top_container_id]] << {"ao_id" => row[:ao_id],
"ao_title" => row[:ao_title],
"level" => row[:level].nil? ? nil : EnumerationValue.filter(:enumeration_value__id => row[:level]).get(:value),
"type2" => row[:type2].nil? ? nil : EnumerationValue.filter(:enumeration_value__id => row[:type2]).get(:value),
"indicator2" => row[:indicator2],
"type3" => row[:type3].nil? ? nil : EnumerationValue.filter(:enumeration_value__id => row[:type3]).get(:value),
"indicator3" => row[:indicator3]
}
end

result
end

def load_archival_objects(ids)
ao_list = ArchivalObject.filter(:id => ids).all

# Our JSONModel(:archival_object) records (keyed on ID)
@ao_json_records = Hash[ArchivalObject.sequel_to_jsonmodel(ao_list).map {|ao| [ao.id, ao.to_hash(:trusted)]}]
end

def load_top_containers(ids)
top_container_list = TopContainer.filter(:id => ids).all
top_container_json_records = Hash[TopContainer.sequel_to_jsonmodel(top_container_list).map {|tc| [tc.id, tc.to_hash(:trusted)]}]

@top_container_json_records = URIResolver.resolve_references(top_container_json_records, ['container_locations','repository','collection'])
end

def fetch_top_container(id)
@top_container_json_records.fetch(id)
end

def fetch_archival_object(id)
@ao_json_records.fetch(id)
end

end
8 changes: 6 additions & 2 deletions frontend/controllers/top_containers_labels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ class TopContainersLabelsController < TopContainersController
set_access_control "view_repository" => [:show, :typeahead, :bulk_operations_browse, :print_labels]

def print_labels
post_uri = "/repositories/#{session[:repo_id]}/top_containers_labels/print_labels"

if params[:print_files]
post_uri = "/repositories/#{session[:repo_id]}/top_containers_labels/print_sub_labels"
else
post_uri = "/repositories/#{session[:repo_id]}/top_containers_labels/print_labels"
end

response = JSONModel::HTTP.post_form(URI(post_uri), {"record_uris[]" => Array(params[:record_uris])})

results = ASUtils.json_parse(response.body)
Expand Down
7 changes: 2 additions & 5 deletions frontend/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,15 @@ en:
auto_scale_header: Auto Scale
auto_scale_message: Auto scale will attempt to shrink any label that overflows the available label space. Labels that fit will not be scaled.
barcode: Container Barcode
catalog_location: Catalog Location Name/Code
field_header: Fields
fields_message: Select the fields to include on the label(s). Fields will appear on the label in the same order.
fields_select_all: Select all
indicator: Container Indicator
institution_name: Parent Institution Name
location: Location
location_barcode: Location Barcode
records_management: Print Records Management Labels
records_management_header: Records Management
records_management_id: Records Management ID
records_management_message: Check this box if printing labels for Records Management Transfers
print_files: Print File Labels
print_files_message: Will print Sub Container Labels - i.e. Files, Items, etc.
repository_name: Repository Name
resource_id: Resource/Accession Identifier
resource_title: Resource/Accession Title
Expand Down
Loading

0 comments on commit fa53048

Please sign in to comment.