From c3ddeac4681dce4dabd926b82c367df90756e0c8 Mon Sep 17 00:00:00 2001 From: Galen Charlton Date: Thu, 6 Aug 2015 22:15:54 +0000 Subject: [PATCH 1/1] LP#1482400: when activating a PO, provide better progress updates This patch improves how progress is reported during the activation of a purchase order that has a large number of assets (bibs and copies) to create. Specifically, it: [1] Shows a progress bar during the asset creation phase. [2] Calculates a linear throttle for updating the record counts widget. The value is based on the maximum number of bibs and items that could be created during the activation, divided by 20 (which is an empirically derived number of streaming responses that can be sent back before the XUL client starts having difficulty). [3] During the asset-creation phase, the number of records created is displayed above the progress bar. [4] Adds a title to the progress bar that's displayed during the second phase of order activation. To test, after applying the patch: [1] Create a purchase order that has a large number of brief bibs and copies to create during activation. For example, 30 line items and 60 copies. [2] Activate the purchase order. You should see the following after entering the record import queue and submitting: (a) Progress dialog whose title is "Creating bib, call number, and copy records.." (b) The progress dialog displays and updates text above the progress bar indicating how many records it has processed, e.g., "Bib Records Merged/Import: 25" (b) The record update counts should be updated at a regular frequency. (c) When the asset creation phase is done, the progress bar's title should change to "Activating purchase order..." Signed-off-by: Galen Charlton Signed-off-by: Mike Rylander --- .../lib/OpenILS/Application/Acq/Order.pm | 22 ++++++++++++-- Open-ILS/web/js/dojo/openils/acq/nls/acq.js | 10 ++++++- .../js/dojo/openils/widget/ProgressDialog.js | 16 ++++++++++ .../web/js/ui/default/acq/common/li_table.js | 30 +++++++++++++++++-- Open-ILS/web/js/ui/default/acq/po/view_po.js | 4 ++- 5 files changed, 76 insertions(+), 6 deletions(-) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm index 4dec15d7bd..69be3d71e5 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm @@ -4,6 +4,10 @@ use OpenSRF::AppSession; use OpenSRF::EX qw/:try/; use strict; use warnings; +# empirically derived number of responses we can +# stream back before the XUL client has indigestion +use constant MAX_RESPONSES => 20; + sub new { my($class, %args) = @_; my $self = bless(\%args, $class); @@ -24,6 +28,7 @@ sub new { }; $self->{cache} = {}; $self->throttle(4) unless $self->throttle; + $self->exponential_falloff(1) unless $self->exponential_falloff; $self->{post_proc_queue} = []; $self->{last_respond_progress} = 0; return $self; @@ -39,6 +44,11 @@ sub throttle { $self->{throttle} = $val if $val; return $self->{throttle}; } +sub exponential_falloff { + my($self, $val) = @_; + $self->{exponential_falloff} = $val if defined $val; + return $self->{exponential_falloff}; +} sub respond { my($self, %other_args) = @_; if($self->throttle and not %other_args) { @@ -48,7 +58,7 @@ sub respond { } $self->conn->respond({ %{$self->{args}}, %other_args }); $self->{last_respond_progress} = $self->{args}->{progress}; - $self->throttle($self->throttle * 2) unless $self->throttle >= 256; + $self->throttle($self->throttle * 2) if ($self->exponential_falloff() and $self->throttle < 256); } sub respond_complete { my($self, %other_args) = @_; @@ -74,6 +84,12 @@ sub total { my($self, $val) = @_; $self->{args}->{total} = $val if defined $val; $self->{args}->{maximum} = $self->{args}->{total}; + if ($self->{args}->{maximum}) { + # if a total has been set, space responses linearly + $self->exponential_falloff(0); + $self->throttle(int($self->{args}->{maximum} / MAX_RESPONSES)); + $self->throttle(4) if $self->throttle < 4; + } return $self->{args}->{total}; } sub purchase_order { @@ -1762,7 +1778,9 @@ sub create_po_assets { where => {'+acqpo' => {id => $po_id}} })->[0]->{id}; - $mgr->total(scalar(@$li_ids) + $lid_total); + # maximum number of Vandelay bib actions is twice + # the number line items (queue bib, then create it) + $mgr->total(scalar(@$li_ids) * 2 + $lid_total); create_lineitem_list_assets($mgr, $li_ids, $args->{vandelay}) or return $e->die_event; diff --git a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js index 53793ceac1..0248ad6ef3 100644 --- a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js +++ b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js @@ -103,5 +103,13 @@ "DUPE_PO_NAME_MSG" : "This name is already in use by another PO", "DUPE_PO_NAME_LINK" : "View PO", "PO_NAME_OPTIONAL" : "${0} (optional)", - "LI_EXISTING_COPIES" : "There are ${0} existing copies for this bibliographic record at this location" + "LI_EXISTING_COPIES" : "There are ${0} existing copies for this bibliographic record at this location", + "LI_CREATING_ASSETS" : "Creating bib, call number, and copy records...", + "PO_ACTIVATING" : "Activating purchase order...", + "ACTIVATE_LI_PROCESSED" : "Lineitems Processed: ${0}", + "ACTIVATE_VQBR_PROCESSED" : "Vandelay Records Processed: ${0}", + "ACTIVATE_BIBS_PROCESSED" : "Bib Records Merged/Imported: ${0}", + "ACTIVATE_LID_PROCESSED" : "ACQ Copies Processed: ${0}", + "ACTIVATE_DEBITS_ACCRUED_PROCESSED" : "Debits Encumbered: ${0}", + "ACTIVATE_COPIES_PROCESSED" : "Real Copies Processed: ${0}" } diff --git a/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js b/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js index 7d2d43d34f..977d490e23 100644 --- a/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js +++ b/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js @@ -48,6 +48,22 @@ if(!dojo._hasResource['openils.widget.ProgressDialog']) { } this.inherited(arguments); + }, + + update_message : function(msg) { + if(msg || (msg = this.message) ) { + if(!this.msgDiv) { + this.msgDiv = dojo.create('div', {innerHTML : msg}); + this.containerNode.insertBefore(this.msgDiv, this.progress.domNode); + } else { + this.msgDiv.innerHTML = msg; + } + } else { + if(this.msgDiv) { + this.containerNode.removeChild(this.msgDiv); + this.msgDiv = null; + } + } } } ); diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js index bf4e4e3eee..5d0a841612 100644 --- a/Open-ILS/web/js/ui/default/acq/common/li_table.js +++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js @@ -95,6 +95,7 @@ function AcqLiTable() { } ); this.vlAgent = new VLAgent(); + this.batchProgress = {}; if (dojo.byId('acq-lit-apply-idents')) { dojo.byId('acq-lit-apply-idents').onclick = function() { @@ -2943,13 +2944,16 @@ function AcqLiTable() { this.show('acq-lit-progress-numbers'); var self = this; var vlArgs = (noVl) ? {} : {vandelay : this.vlAgent.values()}; + this.batchProgress = {}; + progressDialog.show(false); + progressDialog.attr("title", localeStrings.LI_CREATING_ASSETS); fieldmapper.standardRequest( ['open-ils.acq', 'open-ils.acq.purchase_order.assets.create'], { async: true, params: [this.authtoken, this.isPO, vlArgs], onresponse: function(r) { var resp = openils.Util.readResponse(r); - self._updateProgressNumbers(resp, !Boolean(onAssetsCreated), onAssetsCreated); + self._updateProgressNumbers(resp, !Boolean(onAssetsCreated), onAssetsCreated, true); } } ); @@ -3249,9 +3253,16 @@ function AcqLiTable() { ); }; - this._updateProgressNumbers = function(resp, reloadOnComplete, onComplete) { + this._updateProgressNumbers = function(resp, reloadOnComplete, onComplete, clearProgressDialog) { + this._updateProgressDialog(resp); this.vlAgent.handleResponse(resp, function(resp, res) { + if (clearProgressDialog) { + progressDialog.update({ "progress": 100}); + progressDialog.update_message(); + progressDialog.hide(); + progressDialog.attr("title", ""); + } if(reloadOnComplete) location.href = location.href; if (onComplete) @@ -3260,6 +3271,21 @@ function AcqLiTable() { ); } + this._updateProgressDialog = function(resp) { + progressDialog.update({ "progress": (resp.progress / resp.total) * 100 }); + var keys = ['li', 'vqbr', 'bibs', 'lid', 'debits_accrued', 'copies']; + for (var i = 0; i < keys.length; i++) { + if (resp[keys[i]] > (this.batchProgress[keys[i]] || 0)) { + progressDialog.update_message( + dojo.string.substitute( + localeStrings["ACTIVATE_" + keys[i].toUpperCase() + "_PROCESSED"], + [ resp[keys[i]] ] + ) + ); + } + } + this.batchProgress = resp; + } this._createPO = function(fields) { var wantall = (fields.create_from == "all"); diff --git a/Open-ILS/web/js/ui/default/acq/po/view_po.js b/Open-ILS/web/js/ui/default/acq/po/view_po.js index dbe7705f7a..9b76c08f8f 100644 --- a/Open-ILS/web/js/ui/default/acq/po/view_po.js +++ b/Open-ILS/web/js/ui/default/acq/po/view_po.js @@ -637,6 +637,7 @@ function activatePoStage2(noAssets) { var want_refresh = false; progressDialog.show(true); + progressDialog.attr("title", localeStrings.PO_ACTIVATING); fieldmapper.standardRequest( ["open-ils.acq", "open-ils.acq.purchase_order.activate"], { "async": true, @@ -650,11 +651,12 @@ function activatePoStage2(noAssets) { } ], "onresponse": function(r) { - progressDialog.hide(); activatePoButton.attr("disabled", false); want_refresh = Boolean(openils.Util.readResponse(r)); }, "oncomplete": function() { + progressDialog.hide(); + progressDialog.attr("title", ""); if (want_refresh) location.href = location.href; } -- 2.43.2