From: Lebbeous Fogle-Weekley Date: Sat, 31 Mar 2012 16:17:40 +0000 (-0400) Subject: New pull list interface taking advantage of flattener for speed, X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=refs%2Fheads%2Fcollab%2Fsenator%2Fsimplified-hold-pull-list;p=working%2FEvergreen.git New pull list interface taking advantage of flattener for speed, and advanced sorting. For now, access it by the "Simplifed Pull List" button along the bottom edge of the existing holds pull list interface (but I think when/if this thing is widely accepted, it should replace the existing interface outright). Signed-off-by: Lebbeous Fogle-Weekley --- diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index 375f93fa5d..0c3bbbc647 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -4766,7 +4766,7 @@ SELECT usr, - + @@ -4829,6 +4829,109 @@ SELECT usr, + + NOW()) + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT ahr.* FROM action.hold_request ahr JOIN (SELECT current_copy, MAX(capture_time) AS capture_time FROM action.hold_request WHERE capture_time IS NOT NULL GROUP BY current_copy)x USING (current_copy, capture_time) @@ -7831,7 +7934,7 @@ SELECT usr, - + @@ -7849,6 +7952,11 @@ SELECT usr, + + + + + diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/FlatFielder.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/FlatFielder.pm index 0e5fc9868d..dc3da2686b 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/FlatFielder.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/FlatFielder.pm @@ -58,8 +58,7 @@ my $_output_handler_dispatch = { "prio" => 0, "code" => sub { $_[0]->content_type("text/html; charset=utf-8"); - print html_ish_output( @_, 'FlatFielder2HTML.xsl' ); - return Apache2::Const::OK; + return html_ish_output( @_, 'FlatFielder2HTML.xsl' ); } }, "application/xml" => { @@ -115,14 +114,25 @@ sub data_to_xml { $fs->setAttribute("FS_key", $args->{key}) if $args->{key}; $dom->setDocumentElement($fs); + my @columns; + my %column_labels; + if (@{$args->{columns}}) { + @columns = @{$args->{columns}}; + if (@{$args->{labels}}) { + my @labels = @{$args->{labels}}; + $column_labels{$columns[$_]} = $labels[$_] for (0..$#labels); + } + } + my $rownum = 1; for my $i (@{$$args{data}}) { my $item = $dom->createElement("row"); $item->setAttribute('ordinal', $rownum); $rownum++; - for my $k (keys %$i) { + @columns = keys %$i unless @columns; + for my $k (@columns) { my $val = $dom->createElement('column'); - $val->setAttribute('name', $k); + $val->setAttribute('name', $column_labels{$k} || $k); $val->appendText($i->{$k}); $item->addChild($val); } @@ -214,6 +224,8 @@ sub handler { $args{key} = $cgi->param('key'); $args{id_field} = $cgi->param('identifier'); $args{label_field} = $cgi->param('label'); + $args{columns} = [ $cgi->param('columns') ]; + $args{labels} = [ $cgi->param('labels') ]; my $fielder = OpenSRF::AppSession->create('open-ils.fielder'); if ($args{map}) { diff --git a/Open-ILS/src/templates/circ/hold_pull_list.tt2 b/Open-ILS/src/templates/circ/hold_pull_list.tt2 new file mode 100644 index 0000000000..69a91286a7 --- /dev/null +++ b/Open-ILS/src/templates/circ/hold_pull_list.tt2 @@ -0,0 +1,91 @@ +[% WRAPPER base.tt2 %] +[% ctx.page_title = 'Hold Pull List' %] + +
+
+
Hold Pull List
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + +
TitleAuthorShelving LocationPatron Name or Alias
+
+[% END %] diff --git a/Open-ILS/web/js/dojo/openils/FlattenerStore.js b/Open-ILS/web/js/dojo/openils/FlattenerStore.js index e5cbdd7764..700d3f2845 100644 --- a/Open-ILS/web/js/dojo/openils/FlattenerStore.js +++ b/Open-ILS/web/js/dojo/openils/FlattenerStore.js @@ -29,6 +29,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { "offset": 0, "baseSort": null, "defaultSort": null, + "sortFieldReMap": null, "constructor": function(/* object */ args) { dojo.mixin(this, args); @@ -51,7 +52,33 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { ); }, - "_prepare_flattener_params": function(req) { + "_remap_sort": function(prepared_sort) { + if (this.sortFieldReMap) { + return prepared_sort.map( + dojo.hitch( + this, function(exp) { + if (typeof exp == "object") { + var key; + for (key in exp) + break; + var newkey = (key in this.sortFieldReMap) ? + this.sortFieldReMap[key] : key; + var o = {}; + o[newkey] = exp[key]; + return o; + } else { + return (exp in this.sortFieldReMap) ? + this.sortFieldReMap[exp] : exp; + } + } + ) + ); + } else { + return prepared_sort; + } + }, + + "_build_flattener_params": function(req) { var params = { "hint": this.fmClass, "ses": openils.User.authtoken @@ -65,31 +92,38 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { params.where = dojo.toJson(where); } else { - var limit = (!isNaN(req.count) && req.count != Infinity) ? - req.count : this.limit; - var offset = (!isNaN(req.start) && req.start != Infinity) ? - req.start : this.offset; - - dojo.mixin( - params, { - "where": dojo.toJson(req.query), - "slo": dojo.toJson({ - "sort": this._prepare_sort(req.sort), - "limit": limit, - "offset": offset - }) - } - ); + params.where = dojo.toJson(req.query); + + var slo = { + "sort": this._remap_sort(this._prepare_sort(req.sort)) + }; + + if (!req.queryOptions.all) { + slo.limit = + (!isNaN(req.count) && req.count != Infinity) ? + req.count : this.limit; + + slo.offset = + (!isNaN(req.start) && req.start != Infinity) ? + req.start : this.offset; + } + + if (req.queryOptions.columns) + params.columns = req.queryOptions.columns; + if (req.queryOptions.labels) + params.labels = req.queryOptions.labels; + + params.slo = dojo.toJson(slo); } - if (this.mapKey) { /* XXX TODO, get a map key */ + if (this.mapKey) { params.key = this.mapKey; } else { params.map = dojo.toJson(this.mapClause); } - for (var key in params) - console.debug("flattener param " + key + " -> " + params[key]); +// for (var key in params) +// console.debug("flattener param " + key + " -> " + params[key]); return params; }, @@ -114,6 +148,94 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { ); }, + "_on_http_error": function(response, ioArgs, req, retry_method) { + if (response.status == 402) { /* 'Payment Required' stands + in for cache miss */ + if (this._retried_map_key_already) { + var e = new FlattenerStoreError( + "Server won't cache flattener map?" + ); + if (typeof req.onError == "function") + req.onError.call(callback_scope, e); + else + throw e; + } else { + this._retried_map_key_already = true; + delete this.mapKey; + if (retry_method) + return this[retry_method](req); + } + } + }, + + "_fetch_prepare": function(req) { + req.queryOptions = req.queryOptions || {}; + req.abort = function() { console.warn("[unimplemented] abort()"); }; + + if (!this.mapKey) + this._get_map_key(); + + return this._build_flattener_params(req); + }, + + "_fetch_execute": function(params,handle_as,mime_type,onload,onerror) { + dojo.xhrPost({ + "url": this._flattener_url, + "content": params, + "handleAs": handle_as, + "sync": false, + "preventCache": true, + "headers": {"Accept": mime_type}, + "load": onload, + "error": onerror + }); + }, + + /* *** Nonstandard but public API - Please think hard about doing + * things the Dojo Way whenever possible before extending the API + * here. *** */ + + /* fetchToPrint() acts like a lot like fetch(), but doesn't call + * onBegin or onComplete. */ + "fetchToPrint": function(req) { + var callback_scope = req.scope || dojo.global; + var post_params; + + try { + post_params = this._fetch_prepare(req); + } catch (E) { + if (typeof req.onError == "function") + req.onError.call(callback_scope, E); + else + throw E; + } + + var process_fetch_all = dojo.hitch( + this, function(text) { + this._retried_map_key_already = false; + + if (typeof req.onComplete == "function") + req.onComplete.call(callback_scope, text, req); + } + ); + + var process_error = dojo.hitch( + this, function(response, ioArgs) { + this._on_http_error(response, ioArgs, req, "fetchToPrint"); + } + ); + + this._fetch_execute( + post_params, + "text", + "text/html", + process_fetch_all, + process_error + ); + + return req; + }, + /* *** Begin dojo.data.api.Read methods *** */ "getValue": function( @@ -223,35 +345,18 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { // onItem a callback that takes each item as we get it // onComplete a callback that takes the list of items // after they're all fetched - // - // The onError callback is ignored for now (haven't thought - // of anything useful to do with it yet). - // - // The Read API also charges this method with adding an abort - // callback to the *req* object for the caller's use, but - // the one we provide does nothing but issue an alert(). - //console.log("fetch(" + dojo.toJson(req) + ")"); var self = this; var callback_scope = req.scope || dojo.global; - - if (!this.mapKey) { - try { - this._get_map_key(); - } catch (E) { - if (req.onError) - req.onError.call(callback_scope, E); - else - throw E; - } - } - - var post_params = this._prepare_flattener_params(req); - - if (!post_params) { - if (typeof req.onComplete == "function") - req.onComplete.call(callback_scope, [], req); - return; + var post_params; + + try { + post_params = this._fetch_prepare(req); + } catch (E) { + if (typeof req.onError == "function") + req.onError.call(callback_scope, E); + else + throw E; } var process_fetch = function(obj, when) { @@ -296,41 +401,21 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { req.onComplete.call(callback_scope, obj, req); }; - req.abort = function() { - throw new FlattenerStoreError( - "The 'abort' operation is not supported" - ); - }; + var process_error = dojo.hitch( + this, function(response, ioArgs) { + this._on_http_error(response, ioArgs, req, "fetch"); + } + ); var fetch_time = this._last_fetch = (new Date().getTime()); - dojo.xhrPost({ - "url": this._flattener_url, - "content": post_params, - "handleAs": "json", - "sync": false, - "preventCache": true, - "headers": {"Accept": "application/json"}, - "load": function(obj) { process_fetch(obj, fetch_time); }, - "error": function(response, ioArgs) { - if (response.status == 402) { /* 'Payment Required' stands - in for cache miss */ - if (self._retried_map_key_already) { - var e = new FlattenerStoreError( - "Server won't cache flattener map?" - ); - if (typeof req.onError == "function") - req.onError.call(callback_scope, e); - else - throw e; - } else { - self._retried_map_key_already = true; - delete self.mapKey; - return self.fetch(req); - } - } - } - }); + this._fetch_execute( + post_params, + "json", + "application/json", + function(obj) { process_fetch(obj, fetch_time); }, + process_error + ); return req; }, @@ -368,7 +453,15 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { return; } - var post_params = this._prepare_flattener_params(keywordArgs); + var post_params; + try { + post_params = this._fetch_prepare(keywordArgs); + } catch (E) { + if (typeof keywordArgs.onError == "function") + keywordArgs.onError.call(callback_scope, E); + else + throw E; + } var process_fetch_one = dojo.hitch( this, function(obj, when) { @@ -404,17 +497,23 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { } ); + var process_error = dojo.hitch( + this, function(response, ioArgs) { + this._on_http_error( + response, ioArgs, keywordArgs, "fetchItemByIdentity" + ); + } + ); + var fetch_time = this._last_fetch = (new Date().getTime()); - dojo.xhrPost({ - "url": this._flattener_url, - "content": post_params, - "handleAs": "json", - "sync": false, - "preventCache": true, - "headers": {"Accept": "application/json"}, - "load": function(obj){ process_fetch_one(obj, fetch_time); } - }); + this._fetch_execute( + post_params, + "json", + "application/json", + function(obj) { process_fetch_one(obj, fetch_time); }, + process_error + ); }, /* dojo.data.api.Write - only very partially implemented, because diff --git a/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js b/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js index a18cd4aba4..0932970c4f 100644 --- a/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js +++ b/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js @@ -17,6 +17,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { "columnReordering": true, "columnPersistKey": null, "showLoadFilter": false, /* use FlattenerFilterDialog */ + "fetchLock": false, /* These potential constructor arguments maybe useful to * FlattenerGrid in their own right, and are passed to @@ -24,6 +25,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { "fmClass": null, "fmIdentifier": null, "mapExtras": null, + "sortFieldReMap": null, "defaultSort": null, /* whatever any part of the UI says will /replace/ this */ "baseSort": null, /* will contains what the columnpicker @@ -158,6 +160,13 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { var field_def = fieldmapper.IDL.fmclasses[hint].field_map[field]; + if (!field_def) { + throw new Error( + "Lost our way in IDL at hint " + hint + + ", field " + field + ); + } + if (field_def["class"] && path.length) { last_field = field; last_hint = hint; @@ -237,6 +246,22 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { ); }, + "_columnOrderingAndLabels": function() { + var labels = []; + var columns = []; + + this.views.views[0].structure.cells[0].forEach( + function(c) { + if (!c.field.match(/^\+/)) { + labels.push(c.name); + columns.push(c.field); + } + } + ); + + return {"labels": labels, "columns": columns}; + }, + "constructor": function(args) { dojo.mixin(this, args); @@ -287,17 +312,22 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { "_finishStartup": function(sortFields) { - this.setStore( + this._setStore( /* Seriously, let's leave this as _setStore. */ new openils.FlattenerStore({ "fmClass": this.fmClass, "fmIdentifier": this.fmIdentifier, "mapClause": (this.mapClause || this._cleanMapForStore(this._generateMap())), "baseSort": this.baseSort, - "defaultSort": this._mapCPSortFields(sortFields) + "defaultSort": this._mapCPSortFields(sortFields), + "sortFieldReMap": this.sortFieldReMap + }), this.query ); + if (!this.fetchLock) + this._refresh(true); + // pick up any column label changes this.columnPicker.reloadStructure(); @@ -352,6 +382,18 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { } }, + "refresh": function() { + this.fetchLock = false; + this._refresh(/* isRender */ true); + }, + + "_fetch": function() { + if (this.fetchLock) + return; + else + return this.inherited(arguments); + }, + /* ******** below are methods mostly copied but * slightly changed from AutoGrid ******** */ @@ -662,6 +704,26 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { ); } ); + }, + + /* Print the same data that the Flattener is feeding to the + * grid, sorted the same way too. remove limit and offset (i.e., + * print it all. */ + "print": function() { + var coal = this._columnOrderingAndLabels(); + var req = { + "query": this.query, + "queryOptions": { + "all": true, + "columns": coal.columns, + "labels": coal.labels + }, + "onComplete": function(text) { + openils.Util.printHtmlString(text); + } + }; + + this.store.fetchToPrint(req); } } ); diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index e9385ddb22..40157feba5 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -3245,8 +3245,8 @@ - - + + diff --git a/Open-ILS/xul/staff_client/server/patron/holds.js b/Open-ILS/xul/staff_client/server/patron/holds.js index cb45955da9..927aa6d8e1 100644 --- a/Open-ILS/xul/staff_client/server/patron/holds.js +++ b/Open-ILS/xul/staff_client/server/patron/holds.js @@ -352,7 +352,7 @@ patron.holds.prototype = { } } ], - 'cmd_holds_print_alt' : [ + 'cmd_simplified_pull_list' : [ ['command'], function() { try { @@ -376,12 +376,13 @@ patron.holds.prototype = { }); var loc = urls.XUL_BROWSER + "?url=" + window.escape( - xulG.url_prefix("/opac/extras/circ/alt_holds_print.html").replace("http:","https:") + xulG.url_prefix("/eg/circ/hold_pull_list").replace("http:","https:") ); xulG.new_tab( loc, { - "tab_name": "Printable Pull List", /* XXX i18n */ - "browser": false + "tab_name": "Simplified Pull List", /* XXX i18n */ + "browser": false, + "show_print_button": false }, content_params ); } catch (E) { @@ -1488,7 +1489,7 @@ patron.holds.prototype = { var x_clear_shelf_widgets = document.getElementById('clear_shelf_widgets'); var x_expired_checkbox = document.getElementById('expired_checkbox'); var x_print_full_pull_list = document.getElementById('print_full_btn'); - var x_print_full_pull_list_alt = document.getElementById('print_alt_btn'); + var x_simplified_pull_list = document.getElementById('simplified_pull_list_btn'); switch(obj.hold_interface_type) { case 'shelf': obj.render_lib_menus({'pickup_lib':true}); @@ -1496,12 +1497,12 @@ patron.holds.prototype = { if (x_lib_type_menu) x_lib_type_menu.hidden = false; if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false; if (x_clear_shelf_widgets) x_clear_shelf_widgets.hidden = false; - if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true; + if (x_simplified_pull_list) x_simplified_pull_list.hidden = true; break; case 'pull' : if (x_fetch_more) x_fetch_more.hidden = false; if (x_print_full_pull_list) x_print_full_pull_list.hidden = false; - if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = false; + if (x_simplified_pull_list) x_simplified_pull_list.hidden = false; if (x_lib_type_menu) x_lib_type_menu.hidden = true; if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true; break; @@ -1509,7 +1510,7 @@ patron.holds.prototype = { obj.render_lib_menus({'pickup_lib':true,'request_lib':true}); if (x_lib_filter_checkbox) x_lib_filter_checkbox.hidden = false; if (x_lib_type_menu) x_lib_type_menu.hidden = false; - if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true; + if (x_simplified_pull_list) x_simplified_pull_list.hidden = true; if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false; break; default: @@ -1518,7 +1519,7 @@ patron.holds.prototype = { if (x_lib_type_menu) x_lib_type_menu.hidden = true; if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true; if (x_show_cancelled_deck) x_show_cancelled_deck.hidden = false; - if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true; + if (x_simplified_pull_list) x_simplified_pull_list.hidden = true; break; } setTimeout( // We do this because render_lib_menus above creates and appends a DOM node, but until this thread exits, it doesn't really happen diff --git a/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul b/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul index 1e050026dd..ff8d1d6390 100644 --- a/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul +++ b/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul @@ -19,7 +19,7 @@ - + @@ -199,7 +199,7 @@