Vandelay: Insufficient Quality Fall-Thru Merge Profile
authorBill Erickson <berick@esilibrary.com>
Fri, 19 Aug 2011 15:20:55 +0000 (11:20 -0400)
committerMike Rylander <mrylander@gmail.com>
Tue, 23 Aug 2011 18:12:10 +0000 (14:12 -0400)
When a user selects "Merge On Single Match" or "Merge On Best Match" and
apply a minimum quality ratio, the fall-through profile will be used in
a secondary merge attempt for any records that fail the initial merge
due to insufficient record quality.

This allows the user to choose 2 different outcomes based on record
quality.  For example, you may wish to do a full overlay for all
high-quality records, but only extract subjects or other information
from lower quality records.  This also allows users to do record+copy
imports for high-quality records and copy-only imports for low quality
records (using a no-op merge profile like "replace 901c").

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
Open-ILS/web/js/ui/default/vandelay/vandelay.js
Open-ILS/web/opac/locale/en-US/vandelay.dtd
Open-ILS/web/templates/default/vandelay/inc/queue.tt2
Open-ILS/web/templates/default/vandelay/inc/upload.tt2

index 2b563a8..62dca2e 100644 (file)
@@ -885,6 +885,7 @@ sub import_record_list_impl {
     my $auto_overlay_best = $$args{auto_overlay_best_match};
     my $match_quality_ratio = $$args{match_quality_ratio};
     my $merge_profile = $$args{merge_profile};
+    my $ft_merge_profile = $$args{fall_through_merge_profile};
     my $bib_source = $$args{bib_source};
     my $import_no_match = $$args{import_no_match};
 
@@ -990,40 +991,17 @@ sub import_record_list_impl {
 
                 if( scalar(keys %match_recs) == 1) { # all matches point to the same record
 
-                    # $auto_overlay_best_func will find the 1 match and 
-                    # overlay if the quality ratio allows it
-
-                    my $res = $e->json_query(
-                        {
-                            from => [
-                                $auto_overlay_best_func,
-                                $rec_id, 
-                                $merge_profile,
-                                $match_quality_ratio
-                            ]
-                        }
+                    ($imported, $error, $rec) = try_auto_overlay(
+                        $e, $type,
+                        $report_args, 
+                        $auto_overlay_best_func,
+                        $retrieve_func,
+                        $rec_class,
+                        $rec_id, 
+                        $match_quality_ratio, 
+                        $merge_profile, 
+                        $ft_merge_profile
                     );
-
-                    if($res and ($res = $res->[0])) {
-    
-                        if($res->{$auto_overlay_best_func} eq 't') {
-                            $logger->info("vl: $type overlay-1match succeeded for queued rec $rec_id");
-                            $imported = 1;
-
-                            # re-fetch the record to pick up the imported_as value from the DB
-                            $$report_args{rec} = $rec = $e->$retrieve_func([
-                                $rec_id, {flesh => 1, flesh_fields => {$rec_class => ['matches']}}]);
-
-
-                        } else {
-                            $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
-                            $logger->info("vl: $type overlay-1match failed for queued rec $rec_id");
-                        }
-
-                    } else {
-                        $error = 1;
-                        $logger->error("vl: Error attempting overlay with func=$auto_overlay_best_func, profile=$merge_profile, record=$rec_id");
-                    }
                 }
             }
 
@@ -1063,40 +1041,19 @@ sub import_record_list_impl {
             }
 
             if(!$imported and !$error and $auto_overlay_best and scalar(@{$rec->matches}) > 0 ) {
-
                 # caller says to overlay the best match
 
-                my $res = $e->json_query(
-                    {
-                        from => [
-                            $auto_overlay_best_func,
-                            $rec_id,
-                            $merge_profile,
-                            $match_quality_ratio
-                        ]
-                    }
+                ($imported, $error, $rec) = try_auto_overlay(
+                    $e, $type,
+                    $report_args, 
+                    $auto_overlay_best_func,
+                    $retrieve_func,
+                    $rec_class,
+                    $rec_id, 
+                    $match_quality_ratio, 
+                    $merge_profile, 
+                    $ft_merge_profile
                 );
-
-                if($res and ($res = $res->[0])) {
-
-                    if($res->{$auto_overlay_best_func} eq 't') {
-                        $logger->info("vl: $type auto-overlay-best succeeded for queued rec $rec_id");
-                        $imported = 1;
-
-                        # re-fetch the record to pick up the imported_as value from the DB
-                        $$report_args{rec} = $rec = $e->$retrieve_func([
-                            $rec_id, {flesh => 1, flesh_fields => {$rec_class => ['matches']}}]);
-
-                    } else {
-                        $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
-                        $logger->info("vl: $type auto-overlay-best failed for queued rec $rec_id");
-                    }
-
-                } else {
-                    $error = 1;
-                    $logger->error("vl: Error attempting overlay with func=$auto_overlay_best_func, ".
-                        "quality_ratio=$match_quality_ratio, profile=$merge_profile, record=$rec_id");
-                }
             }
 
             if(!$imported and !$error and $import_no_match and scalar(@{$rec->matches}) == 0) {
@@ -1176,6 +1133,111 @@ sub import_record_list_impl {
     return undef;
 }
 
+
+sub try_auto_overlay {
+    my $e = shift;
+    my $type = shift;
+    my $report_args = shift;
+    my $overlay_func  = shift;
+    my $retrieve_func = shift; 
+    my $rec_class = shift;
+    my $rec_id  = shift;
+    my $match_quality_ratio = shift;
+    my $merge_profile  = shift;
+    my $ft_merge_profile = shift;
+
+    my $imported = 0;
+    my $error = 0;
+
+    # Find the best match and overlay if the quality ratio allows it.
+    my $res = $e->json_query(
+        {
+            from => [
+                $overlay_func,
+                $rec_id, 
+                $merge_profile,
+                $match_quality_ratio
+            ]
+        }
+    );
+
+    if($res and ($res = $res->[0])) {
+
+        if($res->{$overlay_func} eq 't') {
+
+            # first attempt succeeded
+            $imported = 1;
+
+        } else {
+
+            # quality-limited merge failed with insufficient quality.  If there is a 
+            # fall-through merge profile, re-do the merge with the alternate profile
+            # and no quality restriction.
+
+            if($ft_merge_profile and $match_quality_ratio > 0) {
+
+                $logger->info("vl: $type auto-merge failed with profile $merge_profile; ".
+                    "re-merging with fall-through profile $ft_merge_profile");
+
+                my $res = $e->json_query(
+                    {
+                        from => [
+                            $overlay_func,
+                            $rec_id, 
+                            $ft_merge_profile,
+                            0 # minimum quality not required
+                        ]
+                    }
+                );
+
+                if($res and ($res = $res->[0])) {
+
+                    if($res->{$overlay_func} eq 't') {
+
+                        # second attempt succeeded
+                        $imported = 1;
+
+                    } else {
+
+                        # failed to merge on second attempt
+                        $logger->info("vl: $type auto-merge with fall-through failed for queued rec $rec_id");
+                    }
+                } else {
+                    
+                    # second attempt died 
+                    $error = 1;
+                    $logger->error("vl: Error attempting overlay with func=$overlay_func, profile=$merge_profile, record=$rec_id");
+                }
+
+            } else { 
+
+                # failed to merge on first attempt, no fall-through was provided
+                $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
+                $logger->info("vl: $type auto-merge failed for queued rec $rec_id");
+            }
+        }
+
+    } else {
+
+        # first attempt died 
+        $error = 1;
+        $logger->error("vl: Error attempting overlay with func=$overlay_func, profile=$merge_profile, record=$rec_id");
+    }
+
+    if($imported) {
+
+        # at least 1 of the attempts succeeded
+        $logger->info("vl: $type auto-merge succeeded for queued rec $rec_id");
+
+        # re-fetch the record to pick up the imported_as value from the DB
+        $$report_args{rec} = $e->$retrieve_func([
+            $rec_id, {flesh => 1, flesh_fields => {$rec_class => ['matches']}}]);
+    }
+
+    return ($imported, $error, $$report_args{rec});
+}
+
+
 # tracks any import errors, commits the current xact, responds to the client
 sub finish_rec_import_attempt {
     my $args = shift;
index 3d6f019..48423c5 100644 (file)
@@ -120,6 +120,16 @@ function vlInit() {
     vlUploadMergeProfile2.searchAttr = 'name';
     vlUploadMergeProfile2.startup();
 
+    vlUploadFtMergeProfile.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
+    vlUploadFtMergeProfile.labelAttr = 'name';
+    vlUploadFtMergeProfile.searchAttr = 'name';
+    vlUploadFtMergeProfile.startup();
+
+    vlUploadFtMergeProfile2.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
+    vlUploadFtMergeProfile2.labelAttr = 'name';
+    vlUploadFtMergeProfile2.searchAttr = 'name';
+    vlUploadFtMergeProfile2.startup();
+
 
     // Fetch the bib and authority attribute definitions 
     vlFetchBibAttrDefs(function () { checkInitDone(); });
@@ -1098,6 +1108,7 @@ function vlHandleQueueItemsAction(action) {
             vlUploadQueueAutoOverlayExact.attr('value',  vlUploadQueueAutoOverlayExact2.attr('value'));
             vlUploadQueueAutoOverlay1Match.attr('value',  vlUploadQueueAutoOverlay1Match2.attr('value'));
             vlUploadMergeProfile.attr('value',  vlUploadMergeProfile2.attr('value'));
+            vlUploadFtMergeProfile.attr('value',  vlUploadFtMergeProfile2.attr('value'));
             vlUploadQueueAutoOverlayBestMatch.attr('value',  vlUploadQueueAutoOverlayBestMatch2.attr('value'));
             vlUploadQueueAutoOverlayBestMatchRatio.attr('value',  vlUploadQueueAutoOverlayBestMatchRatio2.attr('value'));
 
@@ -1116,6 +1127,8 @@ function vlHandleQueueItemsAction(action) {
             vlUploadQueueAutoOverlay1Match2.attr('value', false);
             vlUploadMergeProfile.attr('value', '');
             vlUploadMergeProfile2.attr('value', '');
+            vlUploadFtMergeProfile.attr('value', '');
+            vlUploadFtMergeProfile2.attr('value', '');
             vlUploadQueueAutoOverlayBestMatch.attr('value', false);
             vlUploadQueueAutoOverlayBestMatch2.attr('value', false);
             vlUploadQueueAutoOverlayBestMatchRatio.attr('value', '0.0');
@@ -1196,6 +1209,12 @@ function vlImportRecordQueue(type, queueId, recList, onload) {
         options.merge_profile = profile;
     }
 
+    var ftprofile = vlUploadFtMergeProfile.attr('value');
+    if(ftprofile != null && ftprofile != '') {
+        options.fall_through_merge_profile = ftprofile;
+    }
+
+
     /* determine which method we're calling */
 
     var method = 'open-ils.vandelay.bib_queue.import';
index aa9649a..99dac5c 100644 (file)
@@ -9,6 +9,7 @@
 <!ENTITY vandelay.auto.import.auto_overlay_best_ratio "Best/Single Match Minimum Quality Ratio">
 <!ENTITY vandelay.auto.import.auto_overlay_best_ratio.desc "New Record Quality / Quality of Best Match">
 <!ENTITY vandelay.auto.import.merge_profile "Merge Profile">
+<!ENTITY vandelay.auto.import.ft_merge_profile "Insufficient Quality Fall-Through Profile">
 <!ENTITY vandelay.auto.width "Auto Width">
 <!ENTITY vandelay.back.to.import.queue "Back To Import Queue">
 <!ENTITY vandelay.bib.attrs "Bibliographic attributes">
index 466ee4f..749434f 100644 (file)
                     </td>
                 </tr>
                 <tr>
+                    <td>&vandelay.auto.import.ft_merge_profile;</td>
+                    <td colspan='4'>
+                        <div jsId='vlUploadFtMergeProfile2' dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
+                    </td>
+                </tr>
+                <tr>
                     <td>
                         <button dojoType='dijit.form.Button' jsId='queueItemsImportCancelButton'>Cancel</button>
                     </td>
index b432252..0c1bbd5 100644 (file)
                 <span style='padding-left: 10px; font-size:90%'>(&vandelay.auto.import.auto_overlay_best_ratio.desc;)</span>
             </td>
         </tr>
+        <tr>
+            <td>&vandelay.auto.import.ft_merge_profile;</td>
+            <td colspan='4'>
+                <div jsId='vlUploadFtMergeProfile' dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
+            </td>
+        </tr>
+
         <tr><td colspan='2' style='border-bottom:2px solid #888;'></td></tr>
         <tr><td colspan='2' style='padding-bottom: 10px;'></td></tr>
         <tr>