Z39.50 Batch Search/Overlay Z-Search UI
authorBill Erickson <berick@esilibrary.com>
Mon, 18 Feb 2013 15:41:01 +0000 (10:41 -0500)
committerDan Wells <dbw2@calvin.edu>
Fri, 7 Jun 2013 18:52:25 +0000 (14:52 -0400)
Adds a new "Find Z39.50 Matches" option to Cataloging -> Manage Record
Buckets.  When selected, the user chooses the Z39 fields, the Z39
sources, the destination queue, and the queue match set.  The user then
submits the search.  Basic progress info is reported to the user.
Once complete, the user can open the destination queue, from which
regular vandelay import, etc. actions may be performed on the newly
found records.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/xul/staff_client/server/cat/bucketz39_dialog.js [new file with mode: 0644]
Open-ILS/xul/staff_client/server/cat/bucketz39_dialog.xul [new file with mode: 0644]
Open-ILS/xul/staff_client/server/cat/bucketz39_dialog_overlay.xul [new file with mode: 0644]
Open-ILS/xul/staff_client/server/cat/record_buckets.js
Open-ILS/xul/staff_client/server/cat/record_buckets_overlay.xul
Open-ILS/xul/staff_client/server/skin/bucketz39.css [new file with mode: 0644]

index 508d6b2..a0ba7bf 100644 (file)
 <!ENTITY staff.search_prefs.clear.label "Clear All">
 <!ENTITY staff.search_prefs.clear.accesskey "C">
 <!ENTITY staff.search_prefs.cleared_message "Preferences cleared">
+<!ENTITY staff.bucketz39_search.dialog_label "Locate Z39.59 Matches">
+<!ENTITY staff.bucketz39_search.servers "Z39.50 Servers:">
+<!ENTITY staff.bucketz39_search.indexes "Z39.50 Search Indexes:">
+<!ENTITY staff.bucketz39_search.queue "Add Results to Queue:">
+<!ENTITY staff.bucketz39_search.match_set "Match Set:">
+<!ENTITY staff.bucketz39_search.match_set_select "-- Select Match Set --">
+<!ENTITY staff.bucketz39_search.perform_search "Perform Search">  
+<!ENTITY staff.bucketz39_search.bib_count "Bib Records to Search:">
+<!ENTITY staff.bucketz39_search.progress "Search Progress:">
+<!ENTITY staff.bucketz39_search.found_matches "Matches Found:">
+<!ENTITY staff.bucketz39_search.close "Close">
+<!ENTITY staff.bucketz39_search.open_queue "Open Queue">
diff --git a/Open-ILS/xul/staff_client/server/cat/bucketz39_dialog.js b/Open-ILS/xul/staff_client/server/cat/bucketz39_dialog.js
new file mode 100644 (file)
index 0000000..0ede94f
--- /dev/null
@@ -0,0 +1,236 @@
+var dialog;
+
+function Bucketz39Dialog() {
+
+    /**
+     * builds the  Z39 sources and Z39 search indexes grid
+    */
+    this._build_options_grid = function(list, key, id_attr, label_attr) {
+
+        // determine the number of columns per row dynamically
+        var grid = dojo.byId(key).parentNode;
+        var colcount = grid.getElementsByTagName('column').length;
+
+        var row;
+        dojo.forEach(list, function(obj, idx) {
+
+            if (idx % colcount == 0) {
+                row = dojo.create('row');
+                dojo.byId(key).appendChild(row);
+            }
+
+            var attrs = {
+                value : obj[id_attr](),
+                label : obj[label_attr]()
+            };
+            attrs[key] = 1;
+
+            row.appendChild(dojo.create('checkbox', attrs));
+        });
+    }
+
+    /**
+     * Fetches needed data
+     */
+    this.init = function() {
+        var self = this;
+        var pcrud = new OpenSRF.ClientSession('open-ils.pcrud');
+
+        // vandelay queues
+        pcrud.request({
+            method : 'open-ils.pcrud.search.vbq.atomic',
+            params : [
+                this.authtoken, 
+                {owner : this.user_id, queue_type : 'bib'}, 
+                {order_by : {vbq : 'name'}}
+            ],
+            oncomplete : function(r) {
+                if (resp = r.recv()) {
+                    var qlist = resp.content();
+                    dojo.forEach(qlist, function(q) {
+                        var attrs = {label : q.name()};
+                        var item = dojo.create('menuitem', attrs);
+                        dojo.byId('queue_selector').appendChild(item);
+                    });
+                }
+            }
+        }).send();
+
+        // z39 index field maps
+        pcrud.request({
+            method : 'open-ils.pcrud.search.czifm.atomic',
+            params : [
+                this.authtoken, 
+                {id : {'!=' : null}}, 
+                {order_by : {czifm : 'label'}}
+            ],
+            oncomplete : function(r) {
+                self._build_options_grid(
+                    r.recv().content(), 
+                    'index_selector', 'id', 'label');
+            }
+        }).send();
+
+        // z39 sources
+        pcrud.request({
+            method : 'open-ils.pcrud.search.czs.atomic',
+            params : [
+                this.authtoken, 
+                {name : {'!=' : null}},
+                {order_by : {czs : 'name'}}
+            ],
+            oncomplete : function(r) {
+                self._build_options_grid(
+                    r.recv().content(), 
+                    'source_selector', 'name', 'label');
+            }
+        }).send();
+
+        pcrud.request({
+            method : 'open-ils.pcrud.search.vms.atomic',
+            params : [this.authtoken, {
+                owner : this._ws_ancestors(),
+                mtype : 'biblio'
+            }],
+            oncomplete : function(r) {
+                var sets = r.recv().content();
+                dojo.forEach(sets, function(set) {
+                    var attrs = {label : set.name(), value : set.id() };
+                    var item = dojo.create('menuitem', attrs);
+                    dojo.byId('match_set_selector').appendChild(item);
+                });
+            }
+        }).send();
+
+    }
+
+    /* my workstation org unit plus ancestors as a flat list */
+    this._ws_ancestors = function() {
+        JSAN.use('OpenILS.data');
+        var data = new OpenILS.data(); 
+        data.stash_retrieve();
+        var org = data.hash.aou[ this.ws_ou ]
+        var org_list = [];
+
+        while (org) {
+            org_list.push(org.id());
+            org = data.hash.aou[org.parent_ou()];
+        }
+        return org_list;
+    }
+
+    /**
+     * extracts params from UI form elements
+     */
+    this._collect_params = function() {
+
+        // request params
+        var params = [this.authtoken, this.bucket_id];
+
+        // Z39 sources
+        params.push(dojo.query('[source_selector]').filter(
+            function(cbox) { return cbox.checked }).map(
+                function(cbox) { return cbox.getAttribute('value') }));
+
+        // indexes
+        params.push(dojo.query('[index_selector]').filter(
+            function(cbox) { return cbox.checked }).map(
+                function(cbox) { return cbox.getAttribute('value') }));
+
+        params.push({
+            // queue name (editable menulist)
+            queue_name : dojo.byId('queue_selector').parentNode.value,
+            // match set ID
+            match_set : dojo.byId('match_set_selector').parentNode.value
+        });
+
+        return params;
+    }
+
+    this.submit = function() {
+        var self = this;
+        
+        // progress labels
+        this.search_bib_count = dojo.byId('search-bib-count');
+        this.search_queue_count = dojo.byId('search-queue-count');
+        this.search_progress = dojo.byId('search-progress');
+
+        // hide submit row
+        dojo.addClass(dojo.byId('search-submit-row'), 'hideme');
+
+        // show progress rows
+        dojo.forEach(
+            dojo.query('.search_result_row'),
+            function(row) { dojo.removeClass(row, 'hideme') }
+        );
+
+        var params = this._collect_params();
+        dump('Submitting z39 search with: ' + js2JSON(params) + '\n');
+
+        var ses = new OpenSRF.ClientSession('open-ils.search');
+        ses.request({
+            method : 'open-ils.search.z3950.bucket_search_queue',
+            params : params,
+            onresponse : function(r) {
+                var resp = r.recv();
+                if (!resp) return;
+                var stat = resp.content();
+
+                dojo.attr(self.search_bib_count, 'value', ''+stat.bre_count);
+                dojo.attr(self.search_queue_count, 'value', ''+stat.queue_count);
+
+                var scount = Number(stat.search_count);
+                if (scount) {
+                    dojo.attr(self.search_progress, 'value', ''+Math.floor(
+                        (Number(stat.search_complete) / scount) * 100
+                    ));
+                }
+
+                // queue object is returned in the final response
+                self.queue = stat.queue;
+            },
+            oncomplete : function() {
+                dojo.removeClass(dojo.byId('final-actions-row'), 'hideme');
+            }
+        }).send();
+    }
+
+    // Open a new XUL tab focused on the Vandelay queue containing the results.
+    this.open_queue = function() {
+        /*
+        labelKey = labelKey || 'menu.cmd_open_conify.tab';
+        var label = offlineStrings.getString(labelKey);
+        */
+        var label = 'MARC Import/Export'; // TODO
+       
+        // URL
+        /*
+        var url_prefix = this.xulG.url_prefix || window.url_prefix;
+        */
+        var urls = this.xulG.urls || window.urls;
+        var loc = urls.XUL_BROWSER + '?url=' + 
+            window.escape(
+                this.xulG.url_prefix('EG_WEB_BASE/') +
+                'vandelay/vandelay?qtype=bib&qid=' + this.queue.id()
+            );
+        
+        var content_params = {
+            'no_xulG': false,
+            'show_print_button': true,
+            'show_nav_buttons': true 
+        };  
+       
+        this.xulG.new_tab(loc, {tab_name: label}, content_params);
+        window.close();
+    }
+}
+
+function my_init() {
+    dialog = new Bucketz39Dialog();
+    dialog.user_id   = window.arguments[0];
+    dialog.authtoken = window.arguments[1];
+    dialog.ws_ou     = window.arguments[2];
+    dialog.bucket_id = window.arguments[3];
+    dialog.xulG      = window.arguments[4];
+    dialog.init();
+}
diff --git a/Open-ILS/xul/staff_client/server/cat/bucketz39_dialog.xul b/Open-ILS/xul/staff_client/server/cat/bucketz39_dialog.xul
new file mode 100644 (file)
index 0000000..552c45b
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?>
+<!-- borrow from the serials css -->
+<?xml-stylesheet href="/xul/server/skin/serial.css" type="text/css"?>
+<?xml-stylesheet href="/xul/server/skin/bucketz39.css" type="text/css"?>
+<!DOCTYPE window PUBLIC "" ""[
+    <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
+<?xul-overlay href="/xul/server/cat/bucketz39_dialog_overlay.xul"?>
+
+<window id="bucketz39_dialog_win"
+    title="Bucket Z39.50 Search Dialog"
+    onload="try{my_init();font_helper();persist_helper();}catch(E){alert(E);}"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+    <script type="text/javascript">
+        var myPackageDir = "open_ils_staff_client";
+        var IAMXUL = true;
+        var g = {};
+    </script>
+
+    <scripts id="openils_util_scripts" />
+
+    <!-- JSAN is still needed for font_helper stuff, but I'm going to try
+        not to use it otherwise.  -->
+    <script type="text/javascript" src="/xul/server/main/JSAN.js" />
+
+    <messagecatalog id="catStrings"
+        src="/xul/server/locale/<!--#echo var='locale'-->/cat.properties" />
+
+    <commandset />
+    <box id="bucketz39_dialog_main" />
+</window>
diff --git a/Open-ILS/xul/staff_client/server/cat/bucketz39_dialog_overlay.xul b/Open-ILS/xul/staff_client/server/cat/bucketz39_dialog_overlay.xul
new file mode 100644 (file)
index 0000000..08e550f
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE overlay PUBLIC "" ""[
+    <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+<overlay id="bucketz39_dialog_overlay"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+    <script type="text/javascript" src="/xul/server/cat/bucketz39_dialog.js" />
+
+    <box orient="vertical" id="bucketz39_dialog_main" flex="1">
+        <caption class="top" label="&staff.bucketz39_search.dialog_label;"/>
+        <vbox flex="1">
+            <grid>                                                     
+                <columns><column /><column /></columns>
+                <rows>      
+                    <row class='search_row'>
+                        <label class='header' 
+                            value="&staff.bucketz39_search.servers;"/>
+                        <grid>
+                            <columns>
+                                <!-- display 2 Z-sources per row -->
+                                <column />
+                                <column />
+                            </columns>
+                            <rows id='source_selector'>
+                            </rows>
+                        </grid>
+                    </row>
+                    <row class='search_row'>
+                        <label class='header' 
+                            value="&staff.bucketz39_search.indexes;"/>
+                        <grid>
+                            <columns>
+                                <!-- display 3 Z-index options per row -->
+                                <column />
+                                <column />
+                                <column />
+                            </columns>
+                            <rows id='index_selector'>
+                            </rows>
+                        </grid>
+                    </row>
+
+                    <row class='search_row'>
+                        <label class='header' 
+                            value="&staff.bucketz39_search.queue;"/>
+                        <menulist editable='true'>
+                            <menupopup id='queue_selector'>
+                            </menupopup>
+                        </menulist>
+                    </row>
+                    <row class='search_row'>
+                        <label class='header' 
+                            value="&staff.bucketz39_search.match_set;"/>
+                        <menulist>
+                            <menupopup id='match_set_selector'>
+                                <menuitem value='' 
+                                    label="&staff.bucketz39_search.match_set_select;"/>
+                            </menupopup>
+                        </menulist>
+                    </row>
+                    <row id='search-submit-row' class='search_row'>
+                        <button oncommand="window.close();" 
+                            icon="remove" accesskey="C" 
+                            label="&common.cancel;" />  
+                        <button oncommand="dialog.submit();" 
+                            icon="accept" accesskey="P" 
+                            label="&staff.bucketz39_search.perform_search;"/>
+                    </row>
+                    <row class='hideme search_result_row search_row'>
+                        <label class='header' 
+                            value='&staff.bucketz39_search.bib_count;'/>
+                        <label id='search-bib-count' value='0'/>
+                    </row>
+                    <row class='hideme search_result_row search_row'>
+                        <label class='header' 
+                            value='&staff.bucketz39_search.found_matches;'/>
+                        <label id='search-queue-count' value='0'/>
+                    </row>
+                    <row class='hideme search_result_row search_row'>
+                        <label class='header' 
+                            value='&staff.bucketz39_search.progress;'/>
+                        <progressmeter id='search-progress' mode='determined'/>
+                    </row>
+                    <row id='final-actions-row' class='hideme search_row'>
+                        <button oncommand="window.close();" 
+                            icon="remove" accesskey="C" 
+                            label="&staff.bucketz39_search.close;"/>
+                        <button oncommand="dialog.open_queue();" 
+                            icon="accept" accesskey="O" 
+                            label="&staff.bucketz39_search.open_queue;"/>
+                    </row>
+                </rows>
+            </grid>
+        </vbox>
+    </box>
+</overlay>
index 393d55a..b6aacd5 100644 (file)
@@ -331,6 +331,7 @@ cat.record_buckets.prototype = {
                                     if (x) x.setAttribute('label','');
                                     obj.controller.view.cmd_record_buckets_delete_bucket.setAttribute('disabled','true');
                                     obj.controller.view.cmd_record_buckets_refresh.setAttribute('disabled','true');
+                                    obj.controller.view.cmd_record_buckets_zsearch.setAttribute('disabled','true');
                                     obj.controller.view.record_buckets_export_records.disabled = true;
                                     obj.controller.view.cmd_merge_records.setAttribute('disabled','true');
                                     obj.controller.view.cmd_delete_records.setAttribute('disabled','true');
@@ -353,6 +354,7 @@ cat.record_buckets.prototype = {
                                     try {
                                         obj.controller.view.cmd_record_buckets_delete_bucket.setAttribute('disabled','false');
                                         obj.controller.view.cmd_record_buckets_refresh.setAttribute('disabled','false');
+                                        obj.controller.view.cmd_record_buckets_zsearch.setAttribute('disabled','false');
                                         obj.controller.view.record_buckets_export_records.disabled = false;
                                         obj.controller.view.cmd_merge_records.setAttribute('disabled','false');
                                         obj.controller.view.cmd_delete_records.setAttribute('disabled','false');
@@ -544,6 +546,7 @@ cat.record_buckets.prototype = {
                                 x.setAttribute('hidden','true');
                                 obj.controller.view.cmd_record_buckets_delete_bucket.setAttribute('disabled','true');
                                 obj.controller.view.cmd_record_buckets_refresh.setAttribute('disabled','true');
+                                obj.controller.view.cmd_record_buckets_zsearch.setAttribute('disabled','true');
                                 obj.controller.view.record_buckets_export_records.disabled = true;
                                 obj.controller.view.cmd_merge_records.setAttribute('disabled','true');
                                 obj.controller.view.cmd_delete_records.setAttribute('disabled','true');
@@ -832,6 +835,28 @@ cat.record_buckets.prototype = {
                             }
                         }
                     ],
+                    'cmd_record_buckets_zsearch' : [
+                        ['command'],
+                        function() {
+                            try {
+                                var bucket_id = obj.controller.view.bucket_menulist.value;
+
+                                window.openDialog(
+                                    // TODO: constants.js
+                                    xulG.url_prefix("oils://remote/xul/server/cat/bucketz39_dialog.xul"),
+                                    "bucketz39_dialog",
+                                    "width=800,height=500",
+                                    obj.data.list.au[0].id(),
+                                    ses(), 
+                                    ses('ws_ou'),
+                                    bucket_id,
+                                    xulG
+                                );
+                            } catch(E) {
+                                alert('Error in record_buckets.js, cmd_record_buckets_zsearch: ' + E);
+                            }
+                        }
+                    ],
 
                     'record_buckets_export_records' : [ ['render'], function(){} ],
                     'record_buckets_list_actions' : [ ['render'], function(){} ]
index 2ea4cdb..0f9215e 100644 (file)
@@ -29,6 +29,7 @@
     <command id="cmd_record_buckets_new_bucket" />
     <command id="cmd_record_buckets_delete_bucket" disabled="true"/>
     <command id="cmd_record_buckets_refresh" disabled="true"/>
+    <command id="cmd_record_buckets_zsearch" disabled="true"/>
 
     <command id="cmd_record_buckets_delete_item" />
     <command id="cmd_record_buckets_to_pending_buckets" />
                                 <menuitem command="cmd_record_buckets_new_bucket" label="&staff.cat.record_buckets_overlay.new_bucket.label;"/>
                                 <menuitem command="cmd_record_buckets_delete_bucket" label="&staff.cat.record_buckets_overlay.delete_bucket.label;"/>
                                 <menuitem command="cmd_record_buckets_refresh" label="&staff.cat.record_buckets_overlay.refresh_bucket.label;"/>
+                                <!-- TODO: label -->
+                                <menuitem command="cmd_record_buckets_zsearch" label="Locate Z39.50 Matches"/>
                             </menupopup>
                         </button>
                         <label id="bucket_item_count" />
diff --git a/Open-ILS/xul/staff_client/server/skin/bucketz39.css b/Open-ILS/xul/staff_client/server/skin/bucketz39.css
new file mode 100644 (file)
index 0000000..401ab0e
--- /dev/null
@@ -0,0 +1,5 @@
+.search_row {
+    border-bottom:1px solid black; 
+    padding:5px;  
+}
+