Teach the staff client volume copy creator to respect OU call number preferences
authordbs <dbs@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Mon, 9 Aug 2010 20:33:26 +0000 (20:33 +0000)
committerdbs <dbs@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Mon, 9 Aug 2010 20:33:26 +0000 (20:33 +0000)
To teach the "Add volumes" dialog in the staff client how to show the correct
set of call numbers based on the workstation OU's default classification
scheme, this commit:

  * Adds a column "field" to the asset.call_number_class table that specifies
    the list of tag/subfield combinations to search for a candidate call number
    for a given classification scheme
  * Adds a new OU setting, 'cat.default_classification_scheme', that points
    to the asset.call_number_class ID
  * Extends the open-ils.cat.biblio.record.marc_cn.retrieve method to support
    a second argument, identifying the classification scheme for the call
    number extraction
  * Fixes the IDL for asset.call_number_class to include the ID as an explicit
    field
  * Makes the "Add volumes" dialog look up the 'cat.default_classification_scheme'
    setting for the workstation OU and apply that to the call to the
    open-ils.cat.biblio.record.marc_cn.retrieve method

TODO:
  * Provide a means of switching the classification scheme for the current volume,
    repopulating the call number selector widget
  * Save the chosen scheme as part of the acn object

git-svn-id: svn://svn.open-ils.org/ILS/trunk@17141 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/asset.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/dbi.pm
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/0365.schema.asset_call_number_class_field.sql [new file with mode: 0644]
Open-ILS/xul/staff_client/server/cat/volume_copy_creator.js

index bae749d..db066c7 100644 (file)
@@ -1625,8 +1625,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
        </class>
     <class id="acnc" controller="open-ils.cstore" oils_obj:fieldmapper="asset::call_number_class" oils_persist:tablename="asset.call_number_class" reporter:label="Call number classification scheme">
         <fields oils_persist:primary="id" oils_persist:sequence="asset.call_number_class_id_seq">
+            <field reporter:label="Call number class ID" name="id" reporter_datatype="id"/>
             <field reporter:label="Name" name="name" reporter:datatype="text"/>
             <field reporter:label="Normalizer function" name="normalizer" reporter:datatype="text"/>
+            <field reporter:label="Call number fields" name="field" reporter:datatype="text"/>
         </fields>
     </class>
        <class id="acn" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::call_number" oils_persist:tablename="asset.call_number" reporter:label="Call Number/Volume">
index f90b7dd..cb2b081 100644 (file)
@@ -347,33 +347,65 @@ sub biblio_record_record_metadata {
 
 
 __PACKAGE__->register_method(
-       method  => "biblio_record_marc_cn",
-       api_name        => "open-ils.cat.biblio.record.marc_cn.retrieve",
-       argc            => 1, #(bib id ) 
+    method    => "biblio_record_marc_cn",
+    api_name    => "open-ils.cat.biblio.record.marc_cn.retrieve",
+    argc        => 1, #(bib id ) 
+    signature => {
+        desc => 'Extracts call number candidates from a bibliographic record',
+        params => [
+            {desc => 'Record ID', type => 'number'},
+            {desc => '(Optional) Classification scheme ID', type => 'number'},
+        ]
+    },
+    return => {desc => 'Hash of candidate call numbers identified by tag' }
 );
 
 sub biblio_record_marc_cn {
-       my( $self, $client, $id ) = @_;
+    my( $self, $client, $id, $class ) = @_;
 
-       my $session = OpenSRF::AppSession->create("open-ils.cstore");
-       my $marc = $session
-               ->request("open-ils.cstore.direct.biblio.record_entry.retrieve", $id )
-               ->gather(1)
-               ->marc;
+    my $e = new_editor();
+    my $marc = $e->retrieve_biblio_record_entry($id)->marc;
 
-       my $doc = XML::LibXML->new->parse_string($marc);
-       $doc->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
-       
-       my @res;
-       for my $tag ( qw/050 055 060 070 080 082 086 088 090 092 096 098 099/ ) {
-               my @node = $doc->findnodes("//marc:datafield[\@tag='$tag']");
-               for my $x (@node) {
-                       my $cn = $x->findvalue("marc:subfield[\@code='a' or \@code='b']");
-                       push @res, {$tag => $cn} if ($cn);
-               }
-       }
+    my $doc = XML::LibXML->new->parse_string($marc);
+    $doc->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
+
+    my @fields;
+    my @res;
+    if ($class) {
+        @fields = split(/,/, $e->retrieve_asset_call_number_class($class)->field);
+    } else {
+        @fields = qw/050ab 055ab 060ab 070ab 080ab 082ab 086ab 088ab 090 092 096 098 099/;
+    }
+
+    # Get field/subfield combos based on acnc value; for example "050ab,055ab"
+
+    foreach my $field (@fields) {
+        my $tag = substr($field, 0, 3);
+        $logger->debug("Tag = $tag");
+        my @node = $doc->findnodes("//marc:datafield[\@tag='$tag']");
+
+        # Now parse the subfields and build up the subfield XPath
+        my @subfields = split(//, substr($field, 3));
+
+        # If they give us no subfields to parse, default to just the 'a'
+        if (!@subfields) {
+            @subfields = ('a');
+        }
+        my $subxpath;
+        foreach my $sf (@subfields) {
+            $subxpath .= "\@code='$sf' or ";
+        }
+        $subxpath = substr($subxpath, 0, -4);
+        $logger->debug("subxpath = $subxpath");
+
+        # Find the contents of the specified subfields
+        foreach my $x (@node) {
+            my $cn = $x->findvalue("marc:subfield[$subxpath]");
+            push @res, {$tag => $cn} if ($cn);
+        }
+    }
 
-       return \@res
+    return \@res;
 }
 
 __PACKAGE__->register_method(
index 63c894d..dca9fe4 100644 (file)
@@ -21,6 +21,14 @@ __PACKAGE__->columns( Primary => qw/id/ );
 __PACKAGE__->columns( Essential => qw/location org position/ );
 
 #-------------------------------------------------------------------------------
+package asset::call_number_class;
+use base qw/asset/;
+
+__PACKAGE__->table( 'asset_call_number_class' );
+__PACKAGE__->columns( Primary => qw/id/ );
+__PACKAGE__->columns( Essential => qw/name normalizer field/ );
+
+#-------------------------------------------------------------------------------
 package asset::call_number;
 use base qw/asset/;
 
index 0cb4b51..f5a0975 100644 (file)
        asset::call_number->sequence( 'asset.call_number_id_seq' );
        
        #---------------------------------------------------------------------
+       package asset::call_number_class;
+       
+       asset::call_number->table( 'asset.call_number_class' );
+       asset::call_number->sequence( 'asset.call_number_class_id_seq' );
+       
+       #---------------------------------------------------------------------
        package asset::copy_location_order;
        
        asset::copy_location_order->table( 'asset.copy_location_order' );
index 3b12cbf..5f22bd7 100644 (file)
@@ -68,7 +68,7 @@ CREATE TABLE config.upgrade_log (
     install_date    TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
 );
 
-INSERT INTO config.upgrade_log (version) VALUES ('0364'); -- dbs
+INSERT INTO config.upgrade_log (version) VALUES ('0365'); -- dbs
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
index 0d91bdd..72d3a14 100644 (file)
@@ -170,8 +170,15 @@ CREATE TABLE asset.uri (
 CREATE TABLE asset.call_number_class (
     id             bigserial     PRIMARY KEY,
     name           TEXT          NOT NULL,
-    normalizer     TEXT          NOT NULL DEFAULT 'asset.normalize_generic'
+    normalizer     TEXT          NOT NULL DEFAULT 'asset.normalize_generic',
+    field          TEXT          NOT NULL DEFAULT '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099'
 );
+COMMENT ON TABLE asset.call_number_class IS $$
+Defines the call number normalization database functions in the "normalizer"
+column and the tag/subfield combinations to use to lookup the call number in
+the "field" column for a given classification scheme. Tag/subfield combinations
+are delimited by commas.
+$$;
 
 CREATE OR REPLACE FUNCTION asset.label_normalizer() RETURNS TRIGGER AS $func$
 DECLARE
@@ -259,9 +266,9 @@ CREATE OR REPLACE FUNCTION asset.label_normalizer_lc(TEXT) RETURNS TEXT AS $func
 $func$ LANGUAGE PLPERLU;
 
 INSERT INTO asset.call_number_class (name, normalizer) VALUES 
-    ('Generic', 'asset.label_normalizer_generic'),
-    ('Dewey (DDC)', 'asset.label_normalizer_dewey'),
-    ('Library of Congress (LC)', 'asset.label_normalizer_lc')
+    ('Generic', 'asset.label_normalizer_generic', '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099'),
+    ('Dewey (DDC)', 'asset.label_normalizer_dewey', '080ab,082ab'),
+    ('Library of Congress (LC)', 'asset.label_normalizer_lc', '050ab,055ab')
 ;
 
 CREATE TABLE asset.call_number (
index a8dab2c..6446844 100644 (file)
@@ -6462,6 +6462,26 @@ VALUES (
     'acpl'
 );
 
+INSERT INTO config.org_unit_setting_type ( name, label, description, datatype, fm_class )
+VALUES (
+    'cat.default_classification_scheme',
+    oils_i18n_gettext(
+        'setting.name',
+        'Cataloging: Default Classification Scheme',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'setting.name',
+        'Defines the default classification scheme for new call numbers: 1 = Generic; 2 = Dewey; 3 = LC',
+        'coust',
+        'descripton'
+        ),
+    'link',
+    'acnc'
+);
+
+
 -- 0355.data.missing_pieces_format.sql
 
 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES 
diff --git a/Open-ILS/src/sql/Pg/upgrade/0365.schema.asset_call_number_class_field.sql b/Open-ILS/src/sql/Pg/upgrade/0365.schema.asset_call_number_class_field.sql
new file mode 100644 (file)
index 0000000..dd40fa4
--- /dev/null
@@ -0,0 +1,32 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0365'); -- dbs
+
+ALTER TABLE asset.call_number_class ADD COLUMN field TEXT NOT NULL DEFAULT '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099';
+
+COMMENT ON TABLE asset.call_number_class IS $$
+Defines the call number normalization database functions in the "normalizer"
+column and the tag/subfield combinations to use to lookup the call number in
+the "field" column for a given classification scheme. Tag/subfield combinations
+are delimited by commas.
+$$;
+
+-- Generic fields
+UPDATE asset.call_number_class
+    SET field = '050ab,055ab,060ab,070ab,080ab,082ab,086ab,088ab,090,092,096,098,099'
+    WHERE id = 1
+;
+
+-- Dewey fields
+UPDATE asset.call_number_class
+    SET field = '080ab,082ab'
+    WHERE id = 2
+;
+
+-- LC fields
+UPDATE asset.call_number_class
+    SET field = '050ab,055ab'
+    WHERE id = 3
+;
+
+COMMIT;
index 66e4172..fd6bdd3 100644 (file)
@@ -45,6 +45,15 @@ function my_init() {
 
         var ou_ids = xul_param('ou_ids',{'concat' : true}) || [];
 
+        // Get the default callnumber classification scheme from OU settings
+        dojo.require('fieldmapper.OrgUtils');
+        var label_class = fieldmapper.aou.fetchOrgSettingDefault(ses('ws_ou'), 'cat.default_classification_scheme');
+
+        // Assign a default value if none was returned 
+        if (!label_class.value) {
+            label_class.value = 1;
+        }
+
         /***********************************************************************************************************/
         /* If we're passed existing_copies, rig up a copy_shortcut object to leverage existing code for rendering the volume labels, etc. 
          * Also make a lookup object for existing copies keyed on org id and callnumber label, and another keyed on copy id. */
@@ -89,53 +98,7 @@ function my_init() {
         /* For the call number drop down */
 
         if (!g.copy_shortcut) {
-            var cn_blob;
-            try {
-                cn_blob = g.network.simple_request('BLOB_MARC_CALLNUMBERS_RETRIEVE',[g.doc_id]);
-            } catch(E) {
-                cn_blob = [];
-            }
-            var hbox = document.getElementById('marc_cn');
-            var ml = util.widgets.make_menulist(
-                util.functional.map_list(
-                    cn_blob,
-                    function(o) {
-                        for (var i in o) {
-                            return [ o[i], i ];
-                        }
-                    }
-                ).sort(
-                    function(a,b) {
-                        a = a[1]; b = b[1];
-                        if (a == '082') return -1; 
-                        if (b == '082') return 1; 
-                        if (a == '092')  return -1; 
-                        if (b == '092')  return 1; 
-                        if (a < b) return -1; 
-                        if (a > b) return 1; 
-                        return 0;
-                    }
-                )
-            ); hbox.appendChild(ml);
-            ml.setAttribute('editable','true');
-            ml.setAttribute('width', '200');
-            var btn = document.createElement('button');
-            btn.setAttribute('label',$('catStrings').getString('staff.cat.volume_copy_creator.my_init.btn.label'));
-            btn.setAttribute('accesskey',$('catStrings').getString('staff.cat.volume_copy_creator.my_init.btn.accesskey'));
-            btn.setAttribute('image','/xul/server/skin/media/images/down_arrow.gif');
-            hbox.appendChild(btn);
-            btn.addEventListener(
-                'command',
-                function() {
-                    var nl = document.getElementsByTagName('textbox');
-                    for (var i = 0; i < nl.length; i++) {
-                        if (nl[i].getAttribute('rel_vert_pos')==2 
-                            && !nl[i].disabled) nl[i].value = ml.value;
-                    }
-                    if (g.last_focus) setTimeout( function() { g.last_focus.focus(); }, 0 );
-                }, 
-                false
-            );
+            g.list_callnumbers(g.doc_id, label_class.value);
         }
 
         /***********************************************************************************************************/
@@ -590,4 +553,52 @@ g.save_prefs = function () {
     }
 }
 
-
+g.list_callnumbers = function(doc_id, label_class) {
+    var cn_blob;
+    try {
+        cn_blob = g.network.simple_request('BLOB_MARC_CALLNUMBERS_RETRIEVE',[g.doc_id, label_class]);
+    } catch(E) {
+        cn_blob = [];
+    }
+    var hbox = document.getElementById('marc_cn');
+    var ml = util.widgets.make_menulist(
+        util.functional.map_list(
+            cn_blob,
+            function(o) {
+                for (var i in o) {
+                    return [ o[i], i ];
+                }
+            }
+        ).sort(
+            function(a,b) {
+                a = a[1]; b = b[1];
+                if (a == '082') return -1; 
+                if (b == '082') return 1; 
+                if (a == '092')  return -1; 
+                if (b == '092')  return 1; 
+                if (a < b) return -1; 
+                if (a > b) return 1; 
+                return 0;
+            }
+        )
+    ); hbox.appendChild(ml);
+    ml.setAttribute('editable','true');
+    ml.setAttribute('width', '200');
+    var btn = document.createElement('button');
+    btn.setAttribute('label',$('catStrings').getString('staff.cat.volume_copy_creator.my_init.btn.label'));
+    btn.setAttribute('accesskey',$('catStrings').getString('staff.cat.volume_copy_creator.my_init.btn.accesskey'));
+    btn.setAttribute('image','/xul/server/skin/media/images/down_arrow.gif');
+    hbox.appendChild(btn);
+    btn.addEventListener(
+        'command',
+        function() {
+            var nl = document.getElementsByTagName('textbox');
+            for (var i = 0; i < nl.length; i++) {
+                if (nl[i].getAttribute('rel_vert_pos')==2 
+                    && !nl[i].disabled) nl[i].value = ml.value;
+            }
+            if (g.last_focus) setTimeout( function() { g.last_focus.focus(); }, 0 );
+        }, 
+        false
+    );
+}