This is the first commit in the copy import development work outlined
here: https://bugs.launchpad.net/evergreen/+bug/
1444644
It copies the Overlay In-process Acquisition Copie code, with
modifications to fit the specificiations outlined on Launchpad.
This process first checks the number of copies belonging to a record,
whose volumes' owning_lib matches the OU specified in the Holdings
Import Profile. If a match is not found, then it searches the OU's
federation for copies. The record with the largest number of copies
is chosen as the record to be overlaid. If there are two records
with the same number of copies, then the record with the largest
biblio.record_entry.id is chosen for overlay.
Finally, if no records have copies relevant to the OU or its
Federation, then the default best match overlay is attempted.
The vandelay.js and its corresponding upload.tt2 file needed
modifications in order to trigger the code that attempts to Auto
Overlay according the the number of copies per OU.
Two new perms were added to control the use of Auto Overlaying
On-order copies and Using Org Copies to Determine Best Match.
The UI modifications to the Vandelay Onorder Overlay Copy Import
Development work are also included in this commit.
Signed-off-by: Liam Whalen <liam.whalen@bc.libraries.coop>
Conflicts:
Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
my $auto_overlay_exact = $$args{auto_overlay_exact};
my $auto_overlay_1match = $$args{auto_overlay_1match};
my $auto_overlay_best = $$args{auto_overlay_best_match};
+ my $auto_overlay_org_unit_copies = $$args{auto_overlay_org_unit_copies};
my $match_quality_ratio = $$args{match_quality_ratio};
my $merge_profile = $$args{merge_profile};
my $ft_merge_profile = $$args{fall_through_merge_profile};
my $overlay_func = 'vandelay.overlay_bib_record';
my $auto_overlay_func = 'vandelay.auto_overlay_bib_record';
my $auto_overlay_best_func = 'vandelay.auto_overlay_bib_record_with_best';
+ my $auto_overlay_org_unit_copies_func = 'vandelay.auto_overlay_org_unit_copies';
my $retrieve_func = 'retrieve_vandelay_queued_bib_record';
my $update_func = 'update_vandelay_queued_bib_record';
my $search_func = 'search_vandelay_queued_bib_record';
my $record;
my $imported = 0;
+
if ($type eq 'bib') {
# strip configured / selected MARC tags from inbound records
);
}
+ if(!$imported and !$error and $auto_overlay_org_unit_copies and scalar(@{$rec->matches}) > 0 ) {
+ # caller says to overlay depending on the number of copies attached to a record, whose OU
+ # matches the OU of the import record's Holding's Import Profile.
+
+ my $perm = 'IMPORT_USE_ORG_UNIT_COPIES';
+ my $rec_ou = $e->requestor->ws_ou;
+
+ if (!$e->allowed($perm, $rec_ou)) {
+ return $e->die_event;
+ }
+
+ ($imported, $error, $rec) = try_auto_overlay(
+ $e, $type,
+ $report_args,
+ $auto_overlay_org_unit_copies_func,
+ $retrieve_func,
+ $rec_class,
+ $rec_id,
+ $match_quality_ratio,
+ $merge_profile,
+ $ft_merge_profile
+ );
+ }
+
if(!$imported and !$error and $import_no_match and scalar(@{$rec->matches}) == 0) {
# No overlay / merge occurred. Do a traditional record import by creating a new record
my $auto_callnumber = {};
my $opp_acq_copy_overlay = $args->{opp_acq_copy_overlay};
+ my $opp_oo_cat_copy_overlay = $args->{opp_oo_cat_copy_overlay};
my @overlaid_copy_ids;
for my $item_id (@$item_ids) {
my $e = new_editor(requestor => $requestor, xact => 1);
$copy = $acqlid->eg_copy_id;
push(@overlaid_copy_ids, $copy->id);
}
+ } elsif ($opp_oo_cat_copy_overlay) { # we are going to "opportunistically" overlay received, On-order catalogue copies
+
+ my $perm = 'IMPORT_ON_ORDER_CAT_COPY';
+ my $rec_ou = $e->requestor->ws_ou;
+
+ if (!$e->allowed($perm, $rec_ou)) {
+ return $e->die_event;
+ }
+
+ my $query;
+ if ($item->copy_number) {
+ $query = [
+ {
+ "status" => OILS_COPY_STATUS_ON_ORDER,
+ "copy_number" => $item->copy_number,
+ "+acn" => [{"owning_lib" => $item->owning_lib},
+ {"record" => $rec->imported_as}]
+ },
+ {
+ "join" => "acn",
+ "flesh" => 1,
+ "flesh_fields" => {
+ "acp" => ["call_number"]
+ }
+ }
+ ];
+ } else {
+ #see if we have copies in vandelay.import_item that
+ #belong to the current record, but do not have $item->copy_number defined
+ #in their on-order records. Retrieve them by create date from oldest to
+ #newest ORDER BY acp.create_date ASC
+ $query = [
+ {
+ "status" => OILS_COPY_STATUS_ON_ORDER,
+ "+acn" => [{"record" => $rec->imported_as},
+ {"owning_lib" => $item->owning_lib}]
+ },
+ {
+ "join" => "acn",
+ "flesh" => 1,
+ "flesh_fields" => {
+ "acp" => ["call_number"]
+ },
+ "order_by" => { "acp" => "create_date" }
+ }
+ ];
+ }
+ # don't overlay the same copy twice
+ $query->[0]{"+acp"}{"id"} = {"not in" => \@overlaid_copy_ids} if @overlaid_copy_ids;
+ if ($copy = $e->search_asset_copy($query)->[0]) {
+ push(@overlaid_copy_ids, $copy->id);
+ }
}
if ($copy) { # we found a copy to overlay
--- /dev/null
+BEGIN;
+
+INSERT INTO permission.perm_list ( code, description ) VALUES
+ ('IMPORT_USE_ORG_UNIT_COPIES', 'Allows users to import records based on the number of org unit copies attached toa record');
+
+INSERT INTO permission.perm_list ( code, description ) VALUES
+ ('IMPORT_ON_ORDER_CAT_COPY', 'Allows users to import copies based on the on-order items attached to a record');
+
+INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
+ SELECT
+ pgt.id, perm.id, aout.depth, FALSE
+ FROM
+ permission.grp_tree pgt,
+ permission.perm_list perm,
+ actor.org_unit_type aout
+ WHERE
+ pgt.name = 'SITKA Staff' AND
+ aout.name = 'Consortium' AND
+ perm.code IN (
+ 'IMPORT_USE_ORG_UNIT_COPIES',
+ 'IMPORT_ON_ORDER_CAT_COPY'
+ );
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_org_unit_copies ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
+DECLARE
+ eg_id BIGINT;
+ match_count INT;
+ rec vandelay.bib_match%ROWTYPE;
+ v_owning_lib INT;
+ owning_fed INT;
+ copy_count INT := 0;
+ max_copy_count INT := 0;
+BEGIN
+
+ PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
+
+ IF FOUND THEN
+ -- RAISE NOTICE 'already imported, cannot auto-overlay'
+ RETURN FALSE;
+ END IF;
+
+ FOR rec IN SELECT * FROM vandelay.bib_match AS vbm
+ WHERE queued_record = import_id
+ ORDER BY vbm.eg_record DESC
+ LOOP
+ FOR v_owning_lib IN SELECT DISTINCT owning_lib
+ FROM vandelay.import_item AS vii
+ INNER JOIN vandelay.queued_bib_record AS vqbr
+ ON vii.record = vqbr.id
+ WHERE vqbr.id = rec.queued_record
+ LOOP
+ SELECT COUNT(acp.id) INTO copy_count
+ FROM asset.copy AS acp
+ INNER JOIN asset.call_number AS acn
+ ON acp.call_number = acn.id
+ WHERE acn.owning_lib = v_owning_lib
+ AND acn.record = rec.eg_record
+ AND acp.deleted = FALSE;
+
+ IF copy_count > max_copy_count THEN
+ max_copy_count := copy_count;
+ eg_id = rec.eg_record;
+ END IF;
+ END LOOP;
+ END LOOP;
+
+ IF max_copy_count = 0 THEN
+ -- No copies for the owing lib, check the federation
+ FOR rec IN SELECT * FROM vandelay.bib_match AS vbm
+ WHERE queued_record = import_id
+ ORDER BY vbm.eg_record DESC
+ LOOP
+ FOR v_owning_lib IN SELECT DISTINCT owning_lib
+ FROM vandelay.import_item AS vii
+ INNER JOIN vandelay.queued_bib_record AS vqbr
+ ON vii.record = vqbr.id
+ WHERE vqbr.id = rec.queued_record
+ LOOP
+ SELECT id INTO owning_fed
+ FROM actor.org_unit AS aou
+ WHERE parent_ou = (SELECT id FROM
+ actor.org_unit WHERE parent_ou IS NULL)
+ AND v_owning_lib IN (SELECT id FROM
+ actor.org_unit_descendants(aou.id));
+
+ SELECT COUNT(acp.id) INTO copy_count
+ FROM asset.copy AS acp
+ INNER JOIN asset.call_number AS acn
+ ON acp.call_number = acn.id
+ WHERE acn.owning_lib IN (SELECT id FROM
+ actor.org_unit_descendants(owning_fed))
+ AND acn.record = rec.eg_record
+ AND acp.deleted = FALSE;
+
+ IF copy_count > max_copy_count THEN
+ max_copy_count := copy_count;
+ eg_id = rec.eg_record;
+ END IF;
+ END LOOP;
+ END LOOP;
+
+ IF max_copy_count = 0 THEN
+ -- Could not detrmine best match via copy count
+ -- fall back to default best match
+ IF (SELECT * FROM vandelay.auto_overlay_bib_record_with_best( import_id, merge_profile_id, lwm_ratio_value_p )) THEN
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END IF;
+ END IF;
+ END IF;
+
+ RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
+END;
+$$ LANGUAGE PLPGSQL;
+
+COMMIT;
<td>[% l('Auto-overlay In-process Acquisition Copies') %]</td>
<td colspan='4'><input jsId='vlUploadQueueAutoOverlayInprocessAcqCopies2' dojoType='dijit.form.CheckBox'/></td>
</tr>
+ <tr>
+ <td>[% l('Auto-overlay On-order Cataloguing Copies') %]</td>
+ <td colspan='4'><input jsId='vlUploadQueueAutoOverlayOnorderCatCopies2' dojoType='dijit.form.CheckBox'/></td>
+ </tr>
+
+ <tr>
+ <td>[% l('Use Org Unit Matching in Copy to Determine Best Match') %]</td>
+ <td colspan='4'>
+ <input jsId='vlUploadQueueAutoOverlayOrgUnitCopies2' dojoType='dijit.form.CheckBox'/>
+ </td>
+ </tr>
<tr>
<td>
<td>[% l('Auto-overlay In-process Acquisitions Copies') %]</td>
<td colspan='4'><input jsId='vlUploadQueueAutoOverlayInprocessAcqCopies' dojoType='dijit.form.CheckBox'/></td>
</tr>
+ <tr>
+ <td>[% l('Auto-overlay On-order Cataloguing Copies') %]</td>
+ <td colspan='4'><input jsId='vlUploadQueueAutoOverlayOnorderCatCopies' dojoType='dijit.form.CheckBox'/></td>
+ </tr>
+ <tr>
+ <td>[% l('Use Org Unit Matching in Copy to Determine Best Match') %]</td>
+ <td colspan='4'>
+ <input jsId='vlUploadQueueAutoOverlayOrgUnitCopies' dojoType='dijit.form.CheckBox'/>
+ </td>
+ </tr>
<tr><td colspan='2' style='border-bottom:2px solid #888;'></td></tr>
<tr><td colspan='2' style='padding-bottom: 10px;'></td></tr>
vlUploadQueueAutoOverlayBestMatch.attr('value', vlUploadQueueAutoOverlayBestMatch2.attr('value'));
vlUploadQueueAutoOverlayBestMatchRatio.attr('value', vlUploadQueueAutoOverlayBestMatchRatio2.attr('value'));
vlUploadQueueAutoOverlayInprocessAcqCopies.attr('value', vlUploadQueueAutoOverlayInprocessAcqCopies2.attr('value'));
+ vlUploadQueueAutoOverlayOnorderCatCopies.attr('value', vlUploadQueueAutoOverlayOnorderCatCopies2.attr('value'));
+ vlUploadQueueAutoOverlayOrgUnitCopies.attr('value', vlUploadQueueAutoOverlayOrgUnitCopies2.attr('value'));
// attr('value') and various other incantations won't let me set
// the value on the checkedmultiselect, so we temporarily swap
vlUploadQueueAutoOverlayBestMatchRatio2.attr('value', '0.0');
vlUploadQueueAutoOverlayInprocessAcqCopies.attr('value', false);
vlUploadQueueAutoOverlayInprocessAcqCopies2.attr('value', false);
+ vlUploadQueueAutoOverlayOnorderCatCopies.attr('value', false);
+ vlUploadQueueAutoOverlayOnorderCatCopies2.attr('value', false);
+ vlUploadQueueAutoOverlayOrgUnitCopies.attr('value', false);
+ vlUploadQueueAutoOverlayOrgUnitCopies2.attr('value', false);
// and... swap them back
vlUploadTrashGroups2 = vlUploadTrashGroups;
vlUploadQueueAutoOverlayInprocessAcqCopies.checked = false;
}
+ if(vlUploadQueueAutoOverlayOnorderCatCopies.checked) {
+ options.opp_oo_cat_copy_overlay = true; //"opp" for opportunistic "oo" for on order
+ vlUploadQueueAutoOverlayOnorderCatCopies.checked = false;
+ }
+
+ if(vlUploadQueueAutoOverlayOrgUnitCopies.checked) {
+ options.auto_overlay_org_unit_copies = true;
+ vlUploadQueueAutoOverlayOrgUnitCopies.checked = false;
+ options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
+ }
+
var profile = vlUploadMergeProfile.attr('value');
if(profile != null && profile != '') {
options.merge_profile = profile;
vlUploadQueueImportNoMatch.checked ||
vlUploadQueueAutoOverlayExact.checked ||
vlUploadQueueAutoOverlay1Match.checked ||
- vlUploadQueueAutoOverlayBestMatch.checked ) {
+ vlUploadQueueAutoOverlayBestMatch.checked ||
+ vlUploadQueueAutoOverlayOrgUnitCopies.checked ) {
vlImportRecordQueue(
currentType,