[open-ils-commits] [GIT] Evergreen ILS branch master updated. 6c537fc3a063cbb2d1c13907b3d94b6fcde09e73

Evergreen Git git at git.evergreen-ils.org
Fri Feb 17 10:31:57 EST 2017


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  6c537fc3a063cbb2d1c13907b3d94b6fcde09e73 (commit)
       via  a179a354f332dd88d21db93a2340e0c1950dcb6a (commit)
       via  e991ddaacedd4d248e17f55a8e2cc30eb9dd27ea (commit)
       via  42f56d39e87fa053a3129ffc0e00944b507982c7 (commit)
       via  2fe878cce47978489c0fcee257171a1845767a5b (commit)
       via  58d278769fa6e6f40b1264ed0b951f480b3b54d5 (commit)
       via  150d6c32d475f5eb56a8a3f7ef8eae88a7bedfb1 (commit)
       via  abf20be081e1a4094f515ade4bb39e1ce312cd9f (commit)
       via  2ade97550d6a10e993e2575e55d78fd8efb22c97 (commit)
      from  f9f830e2b59edf36c771f6d13382d6407f5fb932 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 6c537fc3a063cbb2d1c13907b3d94b6fcde09e73
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Fri Feb 17 10:29:33 2017 -0500

    LP#1117808: Stamping upgrade scripts for extend use of merge profiles
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index 78bc5b9..f9c5ccc 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -91,7 +91,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1021', :eg_version); -- Jboyer/kmlussier
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1023', :eg_version); -- gmcharlt/kmlussier
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.more_overlay_funcs.sql b/Open-ILS/src/sql/Pg/upgrade/1022.schema.more_overlay_funcs.sql
similarity index 97%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.schema.more_overlay_funcs.sql
rename to Open-ILS/src/sql/Pg/upgrade/1022.schema.more_overlay_funcs.sql
index e3bfa2e..848e8a0 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.more_overlay_funcs.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/1022.schema.more_overlay_funcs.sql
@@ -1,6 +1,6 @@
 BEGIN;
 
--- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('1022', :eg_version);
 
 CREATE OR REPLACE FUNCTION vandelay.merge_record_xml_using_profile ( incoming_marc TEXT, existing_marc TEXT, merge_profile_id BIGINT ) RETURNS TEXT AS $$
 DECLARE
diff --git a/Open-ILS/src/sql/Pg/upgrade/YYYY.data.default_vmp_ou_setting.sql b/Open-ILS/src/sql/Pg/upgrade/1023.data.default_vmp_ou_settings.sql
similarity index 88%
rename from Open-ILS/src/sql/Pg/upgrade/YYYY.data.default_vmp_ou_setting.sql
rename to Open-ILS/src/sql/Pg/upgrade/1023.data.default_vmp_ou_settings.sql
index ebc8b16..4ea169e 100644
--- a/Open-ILS/src/sql/Pg/upgrade/YYYY.data.default_vmp_ou_setting.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/1023.data.default_vmp_ou_settings.sql
@@ -1,6 +1,6 @@
 BEGIN;
 
--- SELECT evergreen.upgrade_deps_block_check('YYYY', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('1023', :eg_version);
 
 INSERT into config.org_unit_setting_type
 ( name, grp, label, description, datatype, fm_class ) VALUES

commit a179a354f332dd88d21db93a2340e0c1950dcb6a
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Feb 10 11:54:39 2017 -0500

    LP#1117808: release notes for New Access points for MARC Overlay
    
    Test plan for the feature:
    
    [1] Define one or more merge profiles in the MARC import/export
        interface.
    [2] Verify that the Default Merge Profile (Z39.50 and Record Buckets)
        library setting type exists, and that if you set it to a value
        for the workstation library, that it the merge profile selectors in the
        record bucket merge and Z39.50 overlay interfaces default to
        the specified value.
    [3] In the web staff client, create a record bucket containing at least
        two or more similar bibliographic records.  From Bucket View,
        select at least two records and select Merge Selected
        Records from the actions menu.
    [4] Verify that:
        * There is a widget for selecting a merge profile
        * That after choosing a lead record, that the lead record
          view reflects the results of the merge.
        * That the results of the merge are updated whenever you:
          * choose a different merge profile
          * choose a different lead record
          * remove a record from consideration
        * That the lead record's MARC is updated upon clicking the
          'Merge' button in the dialog.
    [5] In the web staff client, mark a bib record for overlay, then
        go to the Z39.50 search interface and perform a search. Select
        a record and click the Overlay button, then verify that:
        * There is a widget for selecting a merge profile
        * That the results of the merge are updated whenever you:
          * choose a different merge profile
        * That after closing the dialog, that the overlaid record
          reflects the result of the merge profile.
    [6] Verify that upon exiting and returning to the record bucket
        merge or Z39.50 overlay interfaces, that the last selected
        merge profile is pre-selected.
    
    Development of this feature was performed by the Equinox Open
    Library Initiative and sponsored by the BC Libraries Cooperative.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/docs/RELEASE_NOTES_NEXT/Cataloging/New_Access_Points_for_MARC_Overlay.adoc b/docs/RELEASE_NOTES_NEXT/Cataloging/New_Access_Points_for_MARC_Overlay.adoc
new file mode 100644
index 0000000..bea5305
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/Cataloging/New_Access_Points_for_MARC_Overlay.adoc
@@ -0,0 +1,13 @@
+New Access Points for MARC Merge/Overlay Profiles
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Catalogers can now select a MARC merge/overlay profile to apply when
+merging records in the (browser client) record bucket merge and Z39.50
+record overlay interfaces. In both interfaces, if the user selects
+a merge profile, the results of the merge are displayed, giving the
+user the opportunity to choose a different merge profile or edit
+the records involved prior to committing to the merge.
+
+A new library setting, "Default Merge Profile (Z39.50 and Record Buckets)",
+specifies the merge profile to preselect in the new merge profile
+selectors in the record bucket merge and Z39.50 overlay logs. The
+selectors will also remember the last selection that the user made.

commit e991ddaacedd4d248e17f55a8e2cc30eb9dd27ea
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Dec 23 04:46:47 2016 -0500

    LP#1117808: teach Z39.50 import about merge profiles
    
    This patch adds a widget to the Z39.50 overlay dialog to
    allow the user to select a MARC merge profile to use during
    the merge. For the purpose of applying the merge profile,
    the incoming record from the Z39.50 server is treated as
    the target of the merge, as the user is assumed to be picking
    a Z39.50 record that is "better" overall but may need to be
    supplement with fields from the incumbent record.
    
    As before, the user can choose to edit the incoming record, but
    note that the version that is edited is the /original/ version of
    the Z39.50 record, with any changes due to the merge profile being
    applied after the edit.
    
    Merge profiles that have a 'preserve' specification are excluded
    from selection, as such profiles have the effect of swapping what
    is considered the lead record for the purpose of the MARC merge
    
    This patch also tweaks the dialog to use the egRecordBreaker directive
    to display the incumbent record, as that permits easier copy-and-pasting
    if the user chooses to edit the Z39.50 record.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/cat/z3950/t_overlay.tt2 b/Open-ILS/src/templates/staff/cat/z3950/t_overlay.tt2
index bb51345..e471e20 100644
--- a/Open-ILS/src/templates/staff/cat/z3950/t_overlay.tt2
+++ b/Open-ILS/src/templates/staff/cat/z3950/t_overlay.tt2
@@ -6,17 +6,24 @@
   </div>
   <div class="modal-body">
       <div class="row">
+        <div class="col-xs-3">
+          <label for="merge_profile_selector">[% l('Choose merge profile') %]</label>
+          <eg-fm-value-selector id="merge_profile_selector" ng-model="merge_profile" idl-class="vmp" filter="{'preserve_spec':{'=':null}}" ou-setting="cat.default_merge_profile" sticky-setting="eg.cat.z3950.default_merge_profile"></eg-fm-value-selector>
+           <button class="btn btn-default" ng-click="editOverlayRecord()">
+               [% l('Edit Z39.50 Record') %]
+           </button>
+        </div>
+      </div>
+      <div class="row">
           <div class="col-xs-6">
-              <div>[% l('Replace TCN [_1] ...', '{{overlay_target}}') %]</div>
-              <eg-record-html record-id="overlay_target" />
+              <div>[% l('Replace TCN [_1] ...', '{{overlay_target.id}}') %]</div>
+              <eg-record-breaker marc-xml="overlay_target.orig_marc_xml"></eg-record-breaker>
           </div>
           <div class="col-xs-6">
               <div>[% l('With this?') %]
-                  <button class="btn btn-default" ng-click="editOverlayRecord()">
-                      [% l('Edit') %]
-                  </button>
+                  <span ng-if="overlay_target.merged">[% l('(merged)') %]</span>
               </div>
-              <eg-record-html marc-xml="{{args.marc_xml}}" />
+              <eg-record-breaker marc-xml="overlay_target.marc_xml"></eg-record-breaker>
           </div>
       </div>
   </div>
diff --git a/Open-ILS/web/js/ui/default/staff/cat/z3950/app.js b/Open-ILS/web/js/ui/default/staff/cat/z3950/app.js
index b0bd161..dac7c37 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/z3950/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/z3950/app.js
@@ -306,8 +306,42 @@ function($scope , $q , $location , $timeout , $window,  egCore , egGridDataProvi
             controller:
                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
                 $scope.focusMe = true;
-                $scope.overlay_target = overlay_target;
+                $scope.merge_profile = null;
+                $scope.overlay_target = {
+                    id : overlay_target,
+                    merged : false
+                };
+
+                egCore.pcrud.retrieve('bre', $scope.overlay_target.id)
+                .then(function(rec) {
+                    $scope.overlay_target.orig_marc_xml = rec.marc();
+                    $scope.overlay_target.marc_xml = rec.marc();
+                    $scope.merge_marc(); // in case a sticky value was already set
+                });
+
+                $scope.merge_marc = function() {
+                    if (!$scope.merge_profile) return;
+                    egCore.net.request(
+                        'open-ils.cat',
+                        'open-ils.cat.merge.marc.per_profile',
+                        egCore.auth.token(),
+                        $scope.merge_profile,
+                        [ args.marc_xml, $scope.overlay_target.orig_marc_xml ]
+                    ).then(function(merged) {
+                        if (merged) {
+                            $scope.overlay_target.marc_xml = merged;
+                            $scope.overlay_target.merged = true;
+                        }
+                    });
+                }
+                $scope.$watch('merge_profile', function(newVal, oldVal) {
+                    if (newVal && newVal !== oldVal) {
+                        $scope.merge_marc();
+                    }
+                });
+
                 $scope.args = args;
+                args.overlay_target = $scope.overlay_target;
                 $scope.ok = function(args) { $uibModalInstance.close(args) };
                 $scope.cancel = function () { $uibModalInstance.dismiss() };
                 $scope.editOverlayRecord = function() {
@@ -324,6 +358,7 @@ function($scope , $q , $location , $timeout , $window,  egCore , egGridDataProvi
                             $scope.cancel = function () { $uibModalInstance.dismiss() }
                         }]
                     }).result.then(function (args) {
+                        $scope.merge_marc();
                         if (!args || !args.name) return;
                     });
                 };
@@ -334,7 +369,7 @@ function($scope , $q , $location , $timeout , $window,  egCore , egGridDataProvi
                 'open-ils.cat.biblio.record.marc.replace',
                 egCore.auth.token(),
                 overlay_target,
-                args.marc_xml,
+                (args.overlay_target.merged ? args.overlay_target.marc_xml : args.marc_xml),
                 null, // FIXME bib source
                 null,
                 $scope.selectFieldStripGroups()

commit 42f56d39e87fa053a3129ffc0e00944b507982c7
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Dec 23 01:38:27 2016 -0500

    LP#1117808: teach record bucket merge about merge profiles
    
    This patch adds a widget to the record bucket merge dialog
    to allow the user to select a MARC merge profile to use
    during the merge.
    
    After the user has chosen a lead record, the result of the merge
    is displayed in the left-hand pane, and is updated whenever the user
    
    - changes the selected merge profile
    - swaps in a different lead record
    - removes a subordinate record from consideration
    
    As before, the user can choose to edit the lead record, but note that
    the version that is edited is the /original/ version of the
    lead record, with any changes due to the merge profile
    being applied after the edit.
    
    Since there can be more than one subordinate record in play, the
    result of the merge is calculated by merging the first subordinate
    record into the lead record, then in the second subordinate record,
    and so forth.
    
    Merge profiles that have a 'preserve' specification are excluded
    from selection, as such profiles have the effect of swapping what
    is considered the lead record for the purpose of the MARC merge.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_edit_lead_record.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_edit_lead_record.tt2
index 60f3a09..40c05e1 100644
--- a/Open-ILS/src/templates/staff/cat/bucket/record/t_edit_lead_record.tt2
+++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_edit_lead_record.tt2
@@ -5,8 +5,10 @@
     <h4 class="modal-title">[% l('Edit Lead Record') %]</h4>
   </div>
   <div class="modal-body">
-    <eg-marc-edit-record dirty-flag="dirty_flag" record-id="record_id"
-                         record-type="bre" />
+    <eg-marc-edit-record dirty-flag="dirty_flag" marc-xml="lead.marc_xml"
+                         in-place-mode="true" on-save="on_save"
+                         record-type="bre">
+    </eg-marc-edit-record>
   </div>
   <div class="modal-footer">
     <input type="submit" ng-click="ok()"
diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_merge_records.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_merge_records.tt2
index 76e6409..255d85f 100644
--- a/Open-ILS/src/templates/staff/cat/bucket/record/t_merge_records.tt2
+++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_merge_records.tt2
@@ -6,6 +6,12 @@
   </div>
   <div class="modal-body">
       <div class="row">
+        <div class="col-xs-3">
+          <label for="merge_profile_selector">[% l('Choose merge profile') %]</label>
+          <eg-fm-value-selector id="merge_profile_selector" ng-model="merge_profile" idl-class="vmp" ou-setting="cat.default_merge_profile" filter="{'preserve_spec':{'=':null}}" sticky-setting="eg.cat.record_bucket.default_merge_profile"></eg-fm-value-selector>
+        </div>
+      </div>
+      <div class="row">
           <div class="col-xs-6">
             <h4>[% l('Lead record') %]</h4>
             <div ng-if="lead_id">
@@ -13,11 +19,12 @@
                  <tab heading="[% l('Bib [_1]', '{{lead_id}}') %]">
                    <button class="btn btn-default btn-sm" ng-class="{disabled : editing_inplace}" ng-click="edit_lead_inplace()">[% l('Edit') %]</button>
                    <button class="btn btn-default btn-sm" ng-class="{disabled : editing_inplace}" ng-click="edit_lead()">[% l('Edit using full editor') %]</button>
-                   <eg-marc-edit-record dirty-flag="dirty_flag" record-id="lead_id"
+                   <eg-marc-edit-record dirty-flag="dirty_flag" marc-xml="lead.marc_xml"
+                             in-place-mode="true"
                              record-type="bre" flat-only="true" embedded="true" 
-                             ng-if="editing_inplace" on-save="post_edit_inplace">
+                             ng-show="editing_inplace" on-save="post_edit_inplace">
                    </eg-marc-edit-record>
-                   <eg-record-breaker record-id="lead_id" ng-if="!editing_inplace"></eg-record-breaker>
+                   <eg-record-breaker record-id="lead_id" marc-xml="lead.marc_xml" ng-show="!editing_inplace"></eg-record-breaker>
                    <eg-volume-list record-id="lead_id" edit-copies="true" edit-volumes="true"></eg-volume-list>
                  </tab>
                </tabset>
diff --git a/Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js b/Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js
index 5c891d0..b6c4661 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js
@@ -550,6 +550,8 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
                 $scope.records = [];
                 $scope.lead_id = 0;
+                $scope.merge_profile = null;
+                $scope.lead = { marc_xml : null };
                 $scope.editing_inplace = false;
                 angular.forEach(records, function(rec) {
                     $scope.records.push({ id : rec.id });
@@ -557,16 +559,59 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                 $scope.ok = function() {
                     $uibModalInstance.close({
                         lead_id : $scope.lead_id,
-                        records : $scope.records
+                        records : $scope.records,
+                        merge_profile : $scope.merge_profile,
+                        lead : $scope.lead
                     });
                 }
                 $scope.cancel = function () { $uibModalInstance.dismiss() }
+
+                $scope.merge_marc = function() {
+                    // need lead, at least one sub, and a merge profile
+                    if (!$scope.lead_id) return;
+                    if (!$scope.merge_profile) return;
+
+                    if (!$scope.records.length) {
+                        // if we got here, the last subordinate record
+                        // was likely removed, so let's refresh the
+                        // lead for the sake of a consistent display
+                        egCore.pcrud.retrieve('bre', $scope.lead_id)
+                        .then(function(rec) {
+                            $scope.lead.marc_xml = rec.marc();
+                        });
+                        return;
+                    }
+
+                    var recs = $scope.records.map(function(val) { return val.id; });
+                    recs.unshift($scope.lead_id);
+                    egCore.net.request(
+                        'open-ils.cat',
+                        'open-ils.cat.merge.biblio.per_profile',
+                        egCore.auth.token(),
+                        $scope.merge_profile,
+                        recs
+                    ).then(function(merged) {
+                        if (merged) $scope.lead.marc_xml = merged;
+                    });
+                }
+                $scope.$watch('merge_profile', function(newVal, oldVal) {
+                    if (newVal && newVal !== oldVal) {
+                        $scope.merge_marc();
+                    }
+                });
+
                 $scope.use_as_lead = function(rec) {
                     if ($scope.lead_id) {
                         $scope.records.push({ id : $scope.lead_id });
                     }
                     $scope.lead_id = rec.id;
                     $scope.drop(rec);
+
+                    egCore.pcrud.retrieve('bre', $scope.lead_id)
+                    .then(function(rec) {
+                        $scope.lead.marc_xml = rec.marc();
+                        $scope.merge_marc();
+                    });
                 }
                 $scope.drop = function(rec) {
                     angular.forEach($scope.records, function(val, i) {
@@ -574,6 +619,7 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                             $scope.records.splice(i, 1);
                         }
                     });
+                    $scope.merge_marc();
                 }
                 $scope.post_edit_inplace = function() {
                     $scope.editing_inplace = false;
@@ -582,36 +628,61 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                     $scope.editing_inplace = true;
                 }
                 $scope.edit_lead = function() {
-                    var lead_id = $scope.lead_id;
+                    var lead = { marc_xml : $scope.lead.marc_xml };
+
+                    // passing the on-save callback this way is a
+                    // hack - this invocation of the MARC editor doesn't
+                    // need it, but for some reason using this stomps
+                    // over the callback set by the other MARC editor
+                    // instance
+                    var callback = $scope.post_edit_inplace;
+
                     $uibModal.open({
                         templateUrl: './cat/bucket/record/t_edit_lead_record',
                         size: 'lg',
                         controller:
                             ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
                             $scope.focusMe = true;
-                            $scope.record_id = lead_id;
+                            $scope.lead = lead;
                             $scope.dirty_flag = false;
                             $scope.ok = function() { $uibModalInstance.close() }
                             $scope.cancel = function () { $uibModalInstance.dismiss() }
+                            $scope.on_save = callback;
                         }]
                     }).result.then(function() {
-                        // TODO: need a way to force a refresh of the egRecordBreaker, as
-                        // the record ID does not change
+                        $scope.lead.marc_xml = lead.marc_xml;
                     });
                 };
             }]
         }).result.then(function (args) {
             if (!args.lead_id) return;
             if (!args.records.length) return;
-            egCore.net.request(
-                'open-ils.cat',
-                'open-ils.cat.biblio.records.merge',
-                egCore.auth.token(),
-                args.lead_id,
-                args.records.map(function(val) { return val.id; })
-            ).then(function() {
-                $window.location.href =
-                    egCore.env.basePath + 'cat/catalog/record/' + args.lead_id;
+
+            function update_bib() {
+                if (args.merge_profile) {
+                    return egCore.pcrud.retrieve('bre', args.lead_id)
+                    .then(function(rec) {
+                        rec.marc(args.lead.marc_xml);
+                        rec.edit_date('now');
+                        rec.editor(egCore.auth.user().id());
+                        return egCore.pcrud.update(rec);
+                    });
+                } else {
+                    return $q.when();
+                }
+            }
+
+            update_bib().then(function() {
+                egCore.net.request(
+                    'open-ils.cat',
+                    'open-ils.cat.biblio.records.merge',
+                    egCore.auth.token(),
+                    args.lead_id,
+                    args.records.map(function(val) { return val.id; })
+                ).then(function() {
+                    $window.location.href =
+                        egCore.env.basePath + 'cat/catalog/record/' + args.lead_id;
+                });
             });
         });
     }

commit 2fe878cce47978489c0fcee257171a1845767a5b
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Dec 23 01:37:48 2016 -0500

    LP#1117808: fix marcXml binding for egRecordBreaker
    
    This patch makes the binding of the marcXml attribute
    in the egRecordBreaker directive dynamic.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/staff/cat/services/record.js b/Open-ILS/web/js/ui/default/staff/cat/services/record.js
index 590dea3..81a8cf7 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/services/record.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/services/record.js
@@ -81,7 +81,7 @@ angular.module('egCoreMod')
         template : '<pre>{{breaker}}</pre>',
         scope : {
             recordId : '=',
-            marcXml  : '@',
+            marcXml  : '=',
         },
         link : function(scope, element, attrs) {
             scope.element = angular.element(element);

commit 58d278769fa6e6f40b1264ed0b951f480b3b54d5
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Feb 10 10:49:33 2017 -0500

    LP#1117808: add cat.default_merge_profile library setting
    
    This new library setting specifies the default merge profile
    to select in the record bucket merge and Z39.50 record overlay
    interfaces.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index d6bb2ef..6b49c3c 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -5048,6 +5048,23 @@ INSERT into config.org_unit_setting_type
         'description'
     ),
     'string', null)
+,(
+    'cat.default_merge_profile', 'cat',
+    oils_i18n_gettext(
+        'cat.default_merge_profile',
+        'Default Merge Profile (Z39.50 and Record Buckets)',
+        'coust',
+        'label'
+    ),
+     oils_i18n_gettext(
+        'cat.default_merge_profile',
+        'Default merge profile to use during Z39.50 imports and record bucket merges',
+        'coust',
+        'description'
+    ),
+    'link',
+    'vmp'
+)
 ,( 'opac.browse.pager_shortcuts', 'opac',
     oils_i18n_gettext(
         'opac.browse.pager_shortcuts',
diff --git a/Open-ILS/src/sql/Pg/upgrade/YYYY.data.default_vmp_ou_setting.sql b/Open-ILS/src/sql/Pg/upgrade/YYYY.data.default_vmp_ou_setting.sql
new file mode 100644
index 0000000..ebc8b16
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/YYYY.data.default_vmp_ou_setting.sql
@@ -0,0 +1,25 @@
+BEGIN;
+
+-- SELECT evergreen.upgrade_deps_block_check('YYYY', :eg_version);
+
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+(
+    'cat.default_merge_profile', 'cat',
+    oils_i18n_gettext(
+        'cat.default_merge_profile',
+        'Default Merge Profile (Z39.50 and Record Buckets)',
+        'coust',
+        'label'
+    ),
+     oils_i18n_gettext(
+        'cat.default_merge_profile',
+        'Default merge profile to use during Z39.50 imports and record bucket merges',
+        'coust',
+        'description'
+    ),
+    'link',
+    'vmp'
+);
+
+COMMIT;

commit 150d6c32d475f5eb56a8a3f7ef8eae88a7bedfb1
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Dec 23 01:36:07 2016 -0500

    LP#1117808: new egFmValueSelector directive
    
    This directive supplies a select widget that draws its
    values from a table specified by an IDL class code.  It
    is meant to be used for tables containing a reasonably
    small number of rows (i.e., dozens at most, not hundreds
    or thousands).
    
    The following attributes are available:
    
    idl-class      : Code of IDL class to draw from, e.g., "vmp".
    ng-model       : Model to bind the current selected value to.
    filter         : JSON query filter to apply conditions on the set
                     of rows to be used. (optional)
    sticky-setting : Local storage key to persist the last value
                     that was selected by the user. (optional)
    ou-setting     : Name of library setting to use to set the default
                     selected value. If sticky-setting is used, any
                     previously-selected value will override the default
                     specified by the OU setting. (optional)
    
    The list of values from the source table displayed in the select
    widget are those that meet all of the following conditions:
    
    - primary key is not null
    - filter conditions (if supplied)
    - logged-in user has PCRUD permissions to view the value
    
    The option value is taken from the column in the source table
    specified by the oils_persist:primary IDL attribute, while
    the displayed label is taken from the column that has the
    reporter:selector="name" IDL attribute.
    
    Example:
    
    <eg-fm-value-selector
      idl-class="vmp"
      ng-model="merge_profile"
      filter="{'preserve_spec':{'=':null}}"
      sticky-setting="eg.cat.z3950.selected_merge_profile"
      ou-setting="cat.default.merge_profile"
    ></eg-fm-value-selector>
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/share/t_fm_value_selector.tt2 b/Open-ILS/src/templates/staff/share/t_fm_value_selector.tt2
new file mode 100644
index 0000000..4306e5e
--- /dev/null
+++ b/Open-ILS/src/templates/staff/share/t_fm_value_selector.tt2
@@ -0,0 +1,5 @@
+<select
+  ng-options="item.id as item.name for item in linked_values"
+  ng-model="ngModel"
+  ng-change="handleChange(ngModel)"
+</select>
diff --git a/Open-ILS/web/js/ui/default/staff/services/ui.js b/Open-ILS/web/js/ui/default/staff/services/ui.js
index 3ed6429..e4e4579 100644
--- a/Open-ILS/web/js/ui/default/staff/services/ui.js
+++ b/Open-ILS/web/js/ui/default/staff/services/ui.js
@@ -559,6 +559,97 @@ function($window , egStrings) {
     }
 ])
 
+/*
+ *  egFmValueSelector - widget for selecting a value from list specified
+ *                      by IDL class
+ */
+.directive('egFmValueSelector', function() {
+    return {
+        restrict : 'E',
+        transclude : true,
+        scope : {
+            idlClass : '@',
+            ngModel : '=',
+
+            // optional filter for refining the set of rows that
+            // get returned. Example:
+            //
+            // filter="{'column':{'=':null}}"
+            filter : '=',
+
+            // optional name of settings key for persisting
+            // the last selected value
+            stickySetting : '@',
+
+            // optional OU setting for fetching default value;
+            // used only if sticky setting not set
+            ouSetting : '@'
+        },
+        require: 'ngModel',
+        templateUrl : './share/t_fm_value_selector',
+        controller : ['$scope','egCore', function($scope , egCore) {
+
+            $scope.org = egCore.org; // for use in the link function
+            $scope.auth = egCore.auth; // for use in the link function
+            $scope.hatch = egCore.hatch // for use in the link function
+
+            function flatten_linked_values(cls, list) {
+                var results = [];
+                var fields = egCore.idl.classes[cls].fields;
+                var id_field;
+                var selector;
+                angular.forEach(fields, function(fld) {
+                    if (fld.datatype == 'id') {
+                        id_field = fld.name;
+                        selector = fld.selector ? fld.selector : id_field;
+                        return;
+                    }
+                });
+                angular.forEach(list, function(item) {
+                    var rec = egCore.idl.toHash(item);
+                    results.push({
+                        id : rec[id_field],
+                        name : rec[selector]
+                    });
+                });
+                return results;
+            }
+
+            var search = {};
+            search[egCore.idl.classes[$scope.idlClass].pkey] = {'!=' : null};
+            if ($scope.filter) {
+                angular.extend(search, $scope.filter);
+            }
+            egCore.pcrud.search(
+                $scope.idlClass, search, {}, {atomic : true}
+            ).then(function(list) {
+                $scope.linked_values = flatten_linked_values($scope.idlClass, list);
+            });
+
+            $scope.handleChange = function(value) {
+                if ($scope.stickySetting) {
+                    egCore.hatch.setLocalItem($scope.stickySetting, value);
+                }
+            }
+
+        }],
+        link : function(scope, element, attrs) {
+            if (scope.stickySetting && (angular.isUndefined(scope.ngModel) || (scope.ngModel === null))) {
+                var value = scope.hatch.getLocalItem(scope.stickySetting);
+                scope.ngModel = value;
+            }
+            if (scope.ouSetting && (angular.isUndefined(scope.ngModel) || (scope.ngModel === null))) {
+                scope.org.settings([scope.ouSetting], scope.auth.user().ws_ou())
+                .then(function(set) {
+                    var value = parseInt(set[scope.ouSetting]);
+                    if (!isNaN(value))
+                        scope.ngModel = value;
+                });
+            }
+        }
+    }
+})
+
 .factory('egWorkLog', ['egCore', function(egCore) {
     var service = {};
 

commit abf20be081e1a4094f515ade4bb39e1ce312cd9f
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Nov 30 12:06:18 2016 -0500

    LP#1117808: new methods for calculating MARC merges
    
    This patch adds three new middle-layer methods for calculating
    the result of MARC merges.
    
    open-ils.cat.merge.marc.per_profile:
    
            Calculate the result of merging one or more MARC records
            per the specified merge profile
            @param auth The login session key
            @param merge_profile ID of the record merge profile
            @param records Array of two or more MARCXML records to be
                           merged. If two are supplied, the first
                           is treated as the record to be overlaid,
                           and the the incoming record that will
                           overlay the first. If more than two are
                           supplied, the first is treated as the
                           record to be overlaid, and each following
                           record in turn will be merged into that
                           record.
            @return MARCXML string of the results of the merge
    
    open-ils.cat.merge.biblio.per_profile:
    
            Calculate the result of merging one or more bib records
            per the specified merge profile
    
            @param auth The login session key
            @param merge_profile ID of the record merge profile
            @param records Array of two or more bib record IDs of
                           the bibs to be merged.
            @return MARCXML string of the results of the merge
    
    open-ils.cat.merge.authority.per_profile:
    
            Calculate the result of merging one or more authority records
            per the specified merge profile
    
            @param auth The login session key
            @param merge_profile ID of the record merge profile
            @param records Array of two or more bib record IDs of
                           the bibs to be merged.
            @return MARCXML string of the results of the merge
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm
index 87409e2..f48ff7a 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm
@@ -935,6 +935,122 @@ sub in_db_auth_merge {
 }
 
 __PACKAGE__->register_method(
+    method    => 'calculate_marc_merge',
+    api_name  => 'open-ils.cat.merge.marc.per_profile',
+    signature => q/
+        Calculate the result of merging one or more MARC records
+        per the specified merge profile
+        @param auth The login session key
+        @param merge_profile ID of the record merge profile
+        @param records Array of two or more MARCXML records to be
+                       merged. If two are supplied, the first
+                       is treated as the record to be overlaid,
+                       and the the incoming record that will
+                       overlay the first. If more than two are
+                       supplied, the first is treated as the
+                       record to be overlaid, and each following
+                       record in turn will be merged into that
+                       record.
+        @return MARCXML string of the results of the merge
+    /
+);
+__PACKAGE__->register_method(
+    method    => 'calculate_bib_marc_merge',
+    api_name  => 'open-ils.cat.merge.biblio.per_profile',
+    signature => q/
+        Calculate the result of merging one or more bib records
+        per the specified merge profile
+        @param auth The login session key
+        @param merge_profile ID of the record merge profile
+        @param records Array of two or more bib record IDs of
+                       the bibs to be merged.
+        @return MARCXML string of the results of the merge
+    /
+);
+__PACKAGE__->register_method(
+    method    => 'calculate_authority_marc_merge',
+    api_name  => 'open-ils.cat.merge.authority.per_profile',
+    signature => q/
+        Calculate the result of merging one or more authority records
+        per the specified merge profile
+        @param auth The login session key
+        @param merge_profile ID of the record merge profile
+        @param records Array of two or more bib record IDs of
+                       the bibs to be merged.
+        @return MARCXML string of the results of the merge
+    /
+);
+
+sub _handle_marc_merge {
+    my ($e, $merge_profile_id, $records) = @_;
+
+    my $result = shift @$records;
+    foreach my $incoming (@$records) {
+        my $response = $e->json_query({
+            from => [
+                'vandelay.merge_record_xml_using_profile',
+                $incoming, $result,
+                $merge_profile_id
+            ]
+        });
+        return unless ref($response);
+        $result = $response->[0]->{'vandelay.merge_record_xml_using_profile'};
+    }
+    return $result;
+}
+
+sub calculate_marc_merge {
+    my( $self, $conn, $auth, $merge_profile_id, $records ) = @_;
+
+    my $e = new_editor(authtoken=>$auth, xact=>1);
+    return $e->die_event unless $e->checkauth;
+
+    my $merge_profile = $e->retrieve_vandelay_merge_profile($merge_profile_id)
+        or return $e->die_event;
+    return $e->die_event unless ref($records) && @$records >= 2;
+
+    return _handle_marc_merge($e, $merge_profile_id, $records)
+}
+
+sub calculate_bib_marc_merge {
+    my( $self, $conn, $auth, $merge_profile_id, $bib_ids ) = @_;
+
+    my $e = new_editor(authtoken=>$auth, xact=>1);
+    return $e->die_event unless $e->checkauth;
+
+    my $merge_profile = $e->retrieve_vandelay_merge_profile($merge_profile_id)
+        or return $e->die_event;
+    return $e->die_event unless ref($bib_ids) && @$bib_ids >= 2;
+
+    my $records = [];
+    foreach my $id (@$bib_ids) {
+        my $bre = $e->retrieve_biblio_record_entry($id) or return $e->die_event;
+        push @$records, $bre->marc();
+    }
+
+    return _handle_marc_merge($e, $merge_profile_id, $records)
+}
+
+sub calculate_authority_marc_merge {
+    my( $self, $conn, $auth, $merge_profile_id, $authority_ids ) = @_;
+
+    my $e = new_editor(authtoken=>$auth, xact=>1);
+    return $e->die_event unless $e->checkauth;
+
+    my $merge_profile = $e->retrieve_vandelay_merge_profile($merge_profile_id)
+        or return $e->die_event;
+    return $e->die_event unless ref($authority_ids) && @$authority_ids >= 2;
+
+    my $records = [];
+    foreach my $id (@$authority_ids) {
+        my $are = $e->retrieve_authority_record_entry($id) or return $e->die_event;
+        push @$records, $are->marc();
+    }
+
+    return _handle_marc_merge($e, $merge_profile_id, $records)
+}
+
+__PACKAGE__->register_method(
     method   => "fleshed_volume_update",
     api_name => "open-ils.cat.asset.volume.fleshed.batch.update",);
 

commit 2ade97550d6a10e993e2575e55d78fd8efb22c97
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Nov 30 09:49:10 2016 -0500

    LP#1117808: new Pg function to calculate MARC record merges
    
    This patch adds a new stored function, vandelay.merge_record_xml_using_profile(),
    that when passed two MARCXML strings and the ID of a merge profile.
    returns the MARCXML string representing the results of the record merge.
    
    This is meant for situations where we want to know the results
    of a merge, but not immediately overlay a record in the database.
    
    pgTAP tests are available in t/marc_merge.pg.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index ff474cd..d60e01b 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -1452,6 +1452,60 @@ BEGIN
 END;
 $$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION vandelay.merge_record_xml_using_profile ( incoming_marc TEXT, existing_marc TEXT, merge_profile_id BIGINT ) RETURNS TEXT AS $$
+DECLARE
+    merge_profile   vandelay.merge_profile%ROWTYPE;
+    dyn_profile     vandelay.compile_profile%ROWTYPE;
+    target_marc     TEXT;
+    source_marc     TEXT;
+    replace_rule    TEXT;
+    match_count     INT;
+BEGIN
+
+    IF existing_marc IS NULL OR incoming_marc IS NULL THEN
+        -- RAISE NOTICE 'no marc for source or target records';
+        RETURN NULL;
+    END IF;
+
+    IF merge_profile_id IS NOT NULL THEN
+        SELECT * INTO merge_profile FROM vandelay.merge_profile WHERE id = merge_profile_id;
+        IF FOUND THEN
+            dyn_profile.add_rule := COALESCE(merge_profile.add_spec,'');
+            dyn_profile.strip_rule := COALESCE(merge_profile.strip_spec,'');
+            dyn_profile.replace_rule := COALESCE(merge_profile.replace_spec,'');
+            dyn_profile.preserve_rule := COALESCE(merge_profile.preserve_spec,'');
+        ELSE
+            -- RAISE NOTICE 'merge profile not found';
+            RETURN NULL;
+        END IF;
+    ELSE
+        -- RAISE NOTICE 'no merge profile specified';
+        RETURN NULL;
+    END IF;
+
+    IF dyn_profile.replace_rule <> '' AND dyn_profile.preserve_rule <> '' THEN
+        -- RAISE NOTICE 'both replace [%] and preserve [%] specified', dyn_profile.replace_rule, dyn_profile.preserve_rule;
+        RETURN NULL;
+    END IF;
+
+    IF dyn_profile.replace_rule = '' AND dyn_profile.preserve_rule = '' AND dyn_profile.add_rule = '' AND dyn_profile.strip_rule = '' THEN
+        -- Since we have nothing to do, just return a target record as is
+        RETURN existing_marc;
+    ELSIF dyn_profile.preserve_rule <> '' THEN
+        source_marc = existing_marc;
+        target_marc = incoming_marc;
+        replace_rule = dyn_profile.preserve_rule;
+    ELSE
+        source_marc = incoming_marc;
+        target_marc = existing_marc;
+        replace_rule = dyn_profile.replace_rule;
+    END IF;
+
+    RETURN vandelay.merge_record_xml( target_marc, source_marc, dyn_profile.add_rule, replace_rule, dyn_profile.strip_rule );
+
+END;
+$$ LANGUAGE PLPGSQL;
+
 CREATE OR REPLACE FUNCTION vandelay.template_overlay_bib_record ( v_marc TEXT, eg_id BIGINT) RETURNS BOOL AS $$
     SELECT vandelay.template_overlay_bib_record( $1, $2, NULL);
 $$ LANGUAGE SQL;
diff --git a/Open-ILS/src/sql/Pg/t/marc_merge.pg b/Open-ILS/src/sql/Pg/t/marc_merge.pg
new file mode 100644
index 0000000..e790f7a
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/t/marc_merge.pg
@@ -0,0 +1,128 @@
+BEGIN;
+
+SELECT plan(4);
+
+CREATE OR REPLACE FUNCTION test_marc_merges() RETURNS SETOF TEXT AS $func$
+DECLARE
+    incoming_marc TEXT;
+    existing_marc TEXT;
+    merge_profile_1 BIGINT;
+    merge_profile_2 BIGINT;
+    merge_profile_3 BIGINT;
+    merge_profile_4 BIGINT;
+BEGIN
+    incoming_marc := $$<record    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"    xmlns="http://www.loc.gov/MARC21/slim">
+  <leader>00531nam a2200157 a 4500</leader>
+  <controlfield tag="005">20080729170300.0</controlfield>
+  <controlfield tag="008">      t19981999enka              0 eng  </controlfield>
+  <datafield tag="245" ind1="1" ind2="4">
+    <subfield code="a">merge-example-incoming</subfield>
+  </datafield>
+  <datafield tag="505" ind1="1" ind2="4">
+    <subfield code="a">contents-notes-incoming</subfield>
+  </datafield>
+  <datafield tag="856" ind1="1" ind2="4">
+    <subfield code="u">https://example.org/page-incoming</subfield>
+  </datafield>
+</record>$$;
+    existing_marc := $$<record    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"    xmlns="http://www.loc.gov/MARC21/slim">
+  <leader>00531nam a2200157 a 4500</leader>
+  <controlfield tag="005">20080729170300.0</controlfield>
+  <controlfield tag="008">      t19981999enka              0 eng  </controlfield>
+  <datafield tag="245" ind1="1" ind2="4">
+    <subfield code="a">merge-example-existing</subfield>
+  </datafield>
+  <datafield tag="505" ind1="1" ind2="4">
+    <subfield code="a">contents-notes-existing</subfield>
+  </datafield>
+  <datafield tag="856" ind1="1" ind2="4">
+    <subfield code="u">https://example.org/page-existing</subfield>
+  </datafield>
+</record>$$;
+
+    INSERT INTO vandelay.merge_profile (owner, name, add_spec, replace_spec, strip_spec, preserve_spec)
+        VALUES (1, 'Test MP 1', '505', NULL, NULL, NULL);
+    SELECT CURRVAL('vandelay.merge_profile_id_seq') INTO merge_profile_1;
+    INSERT INTO vandelay.merge_profile (owner, name, add_spec, replace_spec, strip_spec, preserve_spec)
+        VALUES (1, 'Test MP 2', NULL, '505', NULL, NULL);
+    SELECT CURRVAL('vandelay.merge_profile_id_seq') INTO merge_profile_2;
+    INSERT INTO vandelay.merge_profile (owner, name, add_spec, replace_spec, strip_spec, preserve_spec)
+        VALUES (1, 'Test MP 3', NULL, NULL, NULL, '505');
+    SELECT CURRVAL('vandelay.merge_profile_id_seq') INTO merge_profile_3;
+    INSERT INTO vandelay.merge_profile (owner, name, add_spec, replace_spec, strip_spec, preserve_spec)
+        VALUES (1, 'Test MP 4', '856', '505', NULL, NULL);
+    SELECT CURRVAL('vandelay.merge_profile_id_seq') INTO merge_profile_4;
+
+    -- using evergreen.xml_pretty_print(textval::XML)::TEXT to normalize the
+    -- values we're comparing
+    RETURN NEXT is(evergreen.xml_pretty_print(vandelay.merge_record_xml_using_profile(incoming_marc, existing_marc, merge_profile_1)::XML)::TEXT, evergreen.xml_pretty_print($$<record    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"    xmlns="http://www.loc.gov/MARC21/slim">
+  <leader>00531nam a2200157 a 4500</leader>
+  <controlfield tag="005">20080729170300.0</controlfield>
+  <controlfield tag="008">      t19981999enka              0 eng  </controlfield>
+  <datafield tag="245" ind1="1" ind2="4">
+    <subfield code="a">merge-example-existing</subfield>
+  </datafield>
+  <datafield tag="505" ind1="1" ind2="4">
+    <subfield code="a">contents-notes-incoming</subfield>
+  </datafield>
+  <datafield tag="505" ind1="1" ind2="4">
+    <subfield code="a">contents-notes-existing</subfield>
+  </datafield>
+  <datafield tag="856" ind1="1" ind2="4">
+    <subfield code="u">https://example.org/page-existing</subfield>
+  </datafield>
+</record>$$::XML)::TEXT, 'merge with add 505 rule');
+
+    RETURN NEXT is(evergreen.xml_pretty_print(vandelay.merge_record_xml_using_profile(incoming_marc, existing_marc, merge_profile_2)::XML)::TEXT, evergreen.xml_pretty_print($$<record    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"    xmlns="http://www.loc.gov/MARC21/slim">
+  <leader>00531nam a2200157 a 4500</leader>
+  <controlfield tag="005">20080729170300.0</controlfield>
+  <controlfield tag="008">      t19981999enka              0 eng  </controlfield>
+  <datafield tag="245" ind1="1" ind2="4">
+    <subfield code="a">merge-example-existing</subfield>
+  </datafield>
+  <datafield tag="505" ind1="1" ind2="4">
+    <subfield code="a">contents-notes-incoming</subfield>
+  </datafield>
+  <datafield tag="856" ind1="1" ind2="4">
+    <subfield code="u">https://example.org/page-existing</subfield>
+  </datafield>
+</record>$$::XML)::TEXT, 'merge with replace 505 rule');
+
+    RETURN NEXT is(evergreen.xml_pretty_print(vandelay.merge_record_xml_using_profile(incoming_marc, existing_marc, merge_profile_3)::XML)::TEXT, evergreen.xml_pretty_print($$<record    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"    xmlns="http://www.loc.gov/MARC21/slim">
+  <leader>00531nam a2200157 a 4500</leader>
+  <controlfield tag="005">20080729170300.0</controlfield>
+  <controlfield tag="008">      t19981999enka              0 eng  </controlfield>
+  <datafield tag="245" ind1="1" ind2="4">
+    <subfield code="a">merge-example-incoming</subfield>
+  </datafield>
+  <datafield tag="505" ind1="1" ind2="4">
+    <subfield code="a">contents-notes-existing</subfield>
+  </datafield>
+  <datafield tag="856" ind1="1" ind2="4">
+    <subfield code="u">https://example.org/page-incoming</subfield>
+  </datafield>
+</record>$$::XML)::TEXT, 'merge with preserve 505 rule');
+
+    RETURN NEXT is(evergreen.xml_pretty_print(vandelay.merge_record_xml_using_profile(incoming_marc, existing_marc, merge_profile_4)::XML)::TEXT, evergreen.xml_pretty_print($$<record    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"    xmlns="http://www.loc.gov/MARC21/slim">
+  <leader>00531nam a2200157 a 4500</leader>
+  <controlfield tag="005">20080729170300.0</controlfield>
+  <controlfield tag="008">      t19981999enka              0 eng  </controlfield>
+  <datafield tag="245" ind1="1" ind2="4">
+    <subfield code="a">merge-example-existing</subfield>
+  </datafield>
+  <datafield tag="505" ind1="1" ind2="4">
+    <subfield code="a">contents-notes-incoming</subfield>
+  </datafield>
+  <datafield tag="856" ind1="1" ind2="4">
+    <subfield code="u">https://example.org/page-incoming</subfield>
+  </datafield>
+  <datafield tag="856" ind1="1" ind2="4">
+    <subfield code="u">https://example.org/page-existing</subfield>
+  </datafield>
+</record>$$::XML)::TEXT, 'merge with add 856 and replace 505 rule');
+END;
+$func$ LANGUAGE plpgsql;
+
+SELECT * FROM test_marc_merges();
+
+ROLLBACK;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.more_overlay_funcs.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.more_overlay_funcs.sql
new file mode 100644
index 0000000..e3bfa2e
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.more_overlay_funcs.sql
@@ -0,0 +1,59 @@
+BEGIN;
+
+-- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+CREATE OR REPLACE FUNCTION vandelay.merge_record_xml_using_profile ( incoming_marc TEXT, existing_marc TEXT, merge_profile_id BIGINT ) RETURNS TEXT AS $$
+DECLARE
+    merge_profile   vandelay.merge_profile%ROWTYPE;
+    dyn_profile     vandelay.compile_profile%ROWTYPE;
+    target_marc     TEXT;
+    source_marc     TEXT;
+    replace_rule    TEXT;
+    match_count     INT;
+BEGIN
+
+    IF existing_marc IS NULL OR incoming_marc IS NULL THEN
+        -- RAISE NOTICE 'no marc for source or target records';
+        RETURN NULL;
+    END IF;
+
+    IF merge_profile_id IS NOT NULL THEN
+        SELECT * INTO merge_profile FROM vandelay.merge_profile WHERE id = merge_profile_id;
+        IF FOUND THEN
+            dyn_profile.add_rule := COALESCE(merge_profile.add_spec,'');
+            dyn_profile.strip_rule := COALESCE(merge_profile.strip_spec,'');
+            dyn_profile.replace_rule := COALESCE(merge_profile.replace_spec,'');
+            dyn_profile.preserve_rule := COALESCE(merge_profile.preserve_spec,'');
+        ELSE
+            -- RAISE NOTICE 'merge profile not found';
+            RETURN NULL;
+        END IF;
+    ELSE
+        -- RAISE NOTICE 'no merge profile specified';
+        RETURN NULL;
+    END IF;
+
+    IF dyn_profile.replace_rule <> '' AND dyn_profile.preserve_rule <> '' THEN
+        -- RAISE NOTICE 'both replace [%] and preserve [%] specified', dyn_profile.replace_rule, dyn_profile.preserve_rule;
+        RETURN NULL;
+    END IF;
+
+    IF dyn_profile.replace_rule = '' AND dyn_profile.preserve_rule = '' AND dyn_profile.add_rule = '' AND dyn_profile.strip_rule = '' THEN
+        -- Since we have nothing to do, just return a target record as is
+        RETURN existing_marc;
+    ELSIF dyn_profile.preserve_rule <> '' THEN
+        source_marc = existing_marc;
+        target_marc = incoming_marc;
+        replace_rule = dyn_profile.preserve_rule;
+    ELSE
+        source_marc = incoming_marc;
+        target_marc = existing_marc;
+        replace_rule = dyn_profile.replace_rule;
+    END IF;
+
+    RETURN vandelay.merge_record_xml( target_marc, source_marc, dyn_profile.add_rule, replace_rule, dyn_profile.strip_rule );
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+COMMIT;

-----------------------------------------------------------------------

Summary of changes:
 .../src/perlmods/lib/OpenILS/Application/Cat.pm    |  116 ++++++++++++++++++
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    2 +-
 Open-ILS/src/sql/Pg/012.schema.vandelay.sql        |   54 ++++++++
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |   17 +++
 Open-ILS/src/sql/Pg/t/marc_merge.pg                |  128 ++++++++++++++++++++
 .../Pg/upgrade/1022.schema.more_overlay_funcs.sql  |   59 +++++++++
 .../upgrade/1023.data.default_vmp_ou_settings.sql  |   25 ++++
 .../staff/cat/bucket/record/t_edit_lead_record.tt2 |    6 +-
 .../staff/cat/bucket/record/t_merge_records.tt2    |   13 ++-
 .../src/templates/staff/cat/z3950/t_overlay.tt2    |   19 ++-
 .../templates/staff/share/t_fm_value_selector.tt2  |    5 +
 .../js/ui/default/staff/cat/bucket/record/app.js   |   99 +++++++++++++--
 .../web/js/ui/default/staff/cat/services/record.js |    2 +-
 Open-ILS/web/js/ui/default/staff/cat/z3950/app.js  |   39 ++++++-
 Open-ILS/web/js/ui/default/staff/services/ui.js    |   91 ++++++++++++++
 .../New_Access_Points_for_MARC_Overlay.adoc        |   13 ++
 16 files changed, 659 insertions(+), 29 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/t/marc_merge.pg
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/1022.schema.more_overlay_funcs.sql
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/1023.data.default_vmp_ou_settings.sql
 create mode 100644 Open-ILS/src/templates/staff/share/t_fm_value_selector.tt2
 create mode 100644 docs/RELEASE_NOTES_NEXT/Cataloging/New_Access_Points_for_MARC_Overlay.adoc


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list