[open-ils-commits] r18230 - in trunk/Open-ILS: src/perlmods/OpenILS/Application/Circ web/opac/extras/circ (senator)
svn at svn.open-ils.org
svn at svn.open-ils.org
Thu Oct 7 18:37:52 EDT 2010
Author: senator
Date: 2010-10-07 18:37:45 -0400 (Thu, 07 Oct 2010)
New Revision: 18230
Added:
trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.js
Modified:
trunk/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.html
Log:
Expired holds shelf printer needs to be a holds shelf *clearer* and printer
This needs cleaned up and stuff, and made into something cooler.
Basically just does what XUL interfaces under the Circ menu can already do,
but streamlined to tolerate really big datasets.
Much of this code originates from berick and miker.
Modified: trunk/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
===================================================================
--- trunk/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm 2010-10-07 21:40:48 UTC (rev 18229)
+++ trunk/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm 2010-10-07 22:37:45 UTC (rev 18230)
@@ -34,6 +34,8 @@
use DateTime;
use DateTime::Format::ISO8601;
use OpenSRF::Utils qw/:datetime/;
+use Digest::MD5 qw(md5_hex);
+use OpenSRF::Utils::Cache;
my $apputils = "OpenILS::Application::AppUtils";
my $U = $apputils;
@@ -1407,7 +1409,7 @@
(@$sort ? (order_by => $sort) : ()),
($$params{limit} ? (limit => $$params{limit}) : ()),
($$params{offset} ? (offset => $$params{offset}) : ())
- }, {"subquery" => 1}
+ }, {"substream" => 1}
) or return $e->die_event;
$logger->info("about to stream back " . scalar(@$holds_ids) . " holds");
@@ -2754,7 +2756,87 @@
return ( $U->record_to_mvr($title), $volume, $copy, $issuance );
}
+__PACKAGE__->register_method(
+ method => 'clear_shelf_cache',
+ api_name => 'open-ils.circ.hold.clear_shelf.get_cache',
+ stream => 1,
+ signature => {
+ desc => q/
+ Returns the holds processed with the given cache key
+ /
+ }
+);
+sub clear_shelf_cache {
+ my($self, $client, $auth, $cache_key, $chunk_size) = @_;
+ my $e = new_editor(authtoken => $auth, xact => 1);
+ return $e->die_event unless $e->checkauth and $e->allowed('VIEW_HOLD');
+
+ $chunk_size ||= 25;
+ my $hold_data = OpenSRF::Utils::Cache->new('global')->get_cache($cache_key);
+
+ if (!$hold_data) {
+ $logger->info("no hold data found in cache"); # XXX TODO return event
+ $e->rollback;
+ return undef;
+ }
+
+ my $maximum = 0;
+ foreach (keys %$hold_data) {
+ $maximum += scalar(@{ $hold_data->{$_} });
+ }
+ $client->respond({"maximum" => $maximum, "progress" => 0});
+
+ for my $action (sort keys %$hold_data) {
+ while (@{$hold_data->{$action}}) {
+ my @hid_chunk = splice @{$hold_data->{$action}}, 0, $chunk_size;
+
+ my $result_chunk = $e->json_query({
+ "select" => {
+ "acp" => ["barcode"],
+ "au" => [qw/
+ first_given_name second_given_name family_name alias
+ /],
+ "acn" => ["label"],
+ "bre" => ["marc"],
+ "acpl" => ["name"],
+ "ahr" => ["id"]
+ },
+ "from" => {
+ "ahr" => {
+ "acp" => {
+ "field" => "id", "fkey" => "current_copy",
+ "join" => {
+ "acn" => {
+ "field" => "id", "fkey" => "call_number",
+ "join" => {
+ "bre" => {
+ "field" => "id", "fkey" => "record"
+ }
+ }
+ },
+ "acpl" => {"field" => "id", "fkey" => "location"}
+ }
+ },
+ "au" => {"field" => "id", "fkey" => "usr"}
+ }
+ },
+ "where" => {"+ahr" => {"id" => \@hid_chunk}}
+ }, {"substream" => 1}) or return $e->die_event;
+
+ $client->respond([
+ map {
+ +{"action" => $action, "hold_details" => $_}
+ } @$result_chunk
+ ]);
+ }
+ }
+
+ $e->rollback;
+ return undef;
+}
+
+
__PACKAGE__->register_method(
method => 'clear_shelf_process',
stream => 1,
@@ -2776,6 +2858,7 @@
my $e = new_editor(authtoken=>$auth, xact => 1);
$e->checkauth or return $e->die_event;
+ my $cache = OpenSRF::Utils::Cache->new('global');
$org_id ||= $e->requestor->ws_ou;
$e->allowed('UPDATE_HOLD', $org_id) or return $e->die_event;
@@ -2793,8 +2876,9 @@
{ idlist => 1 }
);
-
my @holds;
+ my $chunk_size = 25; # chunked status updates
+ my $counter = 0;
for my $hold_id (@$hold_ids) {
$logger->info("Clear shelf processing hold $hold_id");
@@ -2821,52 +2905,48 @@
}
push(@holds, $hold);
+ $client->respond({maximum => scalar(@holds), progress => $counter}) if ( (++$counter % $chunk_size) == 0);
}
if ($e->commit) {
+ my %cache_data = (
+ hold => [],
+ transit => [],
+ shelf => []
+ );
+
for my $hold (@holds) {
my $copy = $hold->current_copy;
-
my ($alt_hold) = __PACKAGE__->find_nearest_permitted_hold($e, $copy, $e->requestor, 1);
if($alt_hold) {
- # copy is needed for a hold
- $client->respond({action => 'hold', copy => $copy, hold_id => $hold->id});
+ push(@{$cache_data{hold}}, $hold->id); # copy is needed for a hold
} elsif($copy->circ_lib != $e->requestor->ws_ou) {
- # copy needs to transit
- $client->respond({action => 'transit', copy => $copy, hold_id => $hold->id});
+ push(@{$cache_data{transit}}, $hold->id); # copy needs to transit
} else {
- # copy needs to go back to the shelf
- $client->respond({action => 'shelf', copy => $copy, hold_id => $hold->id});
+ push(@{$cache_data{shelf}}, $hold->id); # copy needs to go back to the shelf
}
}
+ my $cache_key = md5_hex(time . $$ . rand());
+ $logger->info("clear_shelf_cache: storing under $cache_key");
+ $cache->put_cache($cache_key, \%cache_data, 7200); # TODO: 2 hours. configurable?
+
# tell the client we're done
- $client->respond_complete;
+ $client->respond_complete({cache_key => $cache_key});
- # fire off the hold cancelation trigger
- my $trigger = OpenSRF::AppSession->connect('open-ils.trigger');
+ # fire off the hold cancelation trigger and wait for response so don't flood the service
+ $U->create_events_for_hook(
+ 'hold_request.cancel.expire_holds_shelf',
+ $_, $org_id, undef, undef, 1) for @holds;
- for my $hold (@holds) {
-
- my $req = $trigger->request(
- 'open-ils.trigger.event.autocreate',
- 'hold_request.cancel.expire_holds_shelf',
- $hold, $org_id);
-
- # wait for response so don't flood the service
- $req->recv;
- }
-
- $trigger->disconnect;
-
} else {
# tell the client we're done
$client->respond_complete;
Modified: trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.html
===================================================================
--- trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.html 2010-10-07 21:40:48 UTC (rev 18229)
+++ trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.html 2010-10-07 22:37:45 UTC (rev 18230)
@@ -8,13 +8,9 @@
@import url('/opac/skin/default/css/layout.css');
</style>
<style type="text/css">
- /* html, body {
- height: 100%;
- width: 100%;
- margin: 0px 0px 0px 0px;
- padding: 0px 0px 0px 0px;
- overflow: hidden;
- } */
+ #clear_holds_deck { margin-bottom: 1em; }
+ a { color: blue; text-decoration: underline; }
+ small { font-size: 9pt; }
body { font-size: 14pt; }
td {
padding-right: 1em;
@@ -44,180 +40,23 @@
<script type="text/javascript" src="/js/dojo/openils/AutoIDL.js"></script>
<script type="text/javascript" src="/js/dojo/openils/User.js"></script>
<script type="text/javascript" src="/js/dojo/openils/Util.js"></script>
+ <script type="text/javascript" src="/opac/extras/circ/alt_holds_print.js"></script>
<script type="text/javascript">
- dojo.require("dojo.cookie");
- dojo.require("dojox.xml.parser");
- dojo.require("openils.BibTemplate");
- dojo.require("openils.widget.ProgressDialog");
-
- function do_pull_list(user, cgi) {
- progress_dialog.show(true);
-
- var any = false;
-
- fieldmapper.standardRequest(
- ['open-ils.circ','open-ils.circ.hold_pull_list.print.stream'],
- { async : true,
- params: [
- user.authtoken,
- { org_id : cgi.param('o'),
- limit : cgi.param('limit'),
- offset : cgi.param('offset'),
- chunk_size : cgi.param('chunk_size'),
- sort : sort_order
- }
- ],
- onresponse : function (r) {
- any = true;
- dojo.forEach( openils.Util.readResponse(r), function (hold_fm) {
-
- // hashify the hold
- var hold = hold_fm.toHash(true);
- hold.usr = hold_fm.usr().toHash(true);
- hold.usr.card = hold_fm.usr().card().toHash(true);
- hold.current_copy = hold_fm.current_copy().toHash(true);
- hold.current_copy.location = hold_fm.current_copy().location().toHash(true);
- hold.current_copy.call_number = hold_fm.current_copy().call_number().toHash(true);
- hold.current_copy.call_number.record = hold_fm.current_copy().call_number().record().toHash(true);
-
- // clone the template's html
- var tr = dojo.clone(
- dojo.query("tr", dojo.byId('template'))[0]
- );
- dojo.query("td:not([type])", tr).forEach(
- function(td) {
- td.innerHTML =
- dojo.string.substitute(td.innerHTML, hold);
- }
- );
-
- new openils.BibTemplate({
- root : tr,
- xml : dojox.xml.parser.parse(hold.current_copy.call_number.record.marc),
- delay: false
- });
-
- dojo.place(tr, "target");
- });
- },
- oncomplete : function () {
- progress_dialog.hide();
- if (any)
- window.print();
- else
- alert(dojo.byId("no_results").innerHTML);
- }
- }
- );
- }
-
- function place_by_sortkey(node, container) {
- /*Don't use a forEach() or anything like that here. too slow.*/
- var sortkey = dojo.attr(node, "sortkey");
- for (var i = 0; i < container.childNodes.length; i++) {
- var rover = container.childNodes[i];
- if (rover.nodeType != 1) continue;
- if (dojo.attr(rover, "sortkey") > sortkey) {
- dojo.place(node, rover, "before");
- return;
- }
- }
- dojo.place(node, container, "last");
- }
-
- function do_shelf_expired_holds(user, cgi) {
- progress_dialog.show(true);
-
- var any = false;
- var target = dojo.byId("target");
- fieldmapper.standardRequest(
- ["open-ils.circ",
- "open-ils.circ.captured_holds.expired.print.stream"], {
- "async": true,
- "params": [
- user.authtoken, {
- "org_id": cgi.param("o"),
- "limit": cgi.param("limit"),
- "offset": cgi.param("offset"),
- "chunk_size": cgi.param("chunk_size"),
- "sort": sort_order
- }
- ],
- "onresponse": function(r) {
- dojo.forEach(
- openils.Util.readResponse(r),
- function(hold_fields) {
- any = true;
- /* munge this object to make it look like
- the template expects */
- var hold = {
- "usr": {},
- "current_copy": {
- "barcode": hold_fields.barcode,
- "call_number": {
- "label": hold_fields.label,
- "record": {"marc": hold_fields.marc}
- },
- "location": {"name": hold_fields.name}
- }
- };
- if (hold_fields.alias) {
- hold.usr.display_name = hold_fields.alias;
- } else {
- hold.usr.display_name = [
- (hold_fields.family_name ? hold_fields.family_name : ""),
- (hold_fields.first_given_name ? hold_fields.first_given_name : ""),
- (hold_fields.second_given_name ? hold_fields.second_given_name : "")
- ].join(" ");
- }
- ["first_given_name","second_given_name","family_name","alias"].forEach(function(k) {hold.usr[k] = hold_fields[k]; });
-
- // clone the template's html
- var tr = dojo.clone(
- dojo.query("tr", dojo.byId('template'))[0]
- );
- dojo.query("td:not([type])", tr).forEach(
- function(td) {
- td.innerHTML =
- dojo.string.substitute(td.innerHTML, hold);
- }
- );
-
- new openils.BibTemplate({
- "root": tr,
- "xml": dojox.xml.parser.parse(hold.current_copy.call_number.record.marc),
- "delay": false
- });
-
- dojo.attr(
- tr, "sortkey", hold.usr.display_name
- );
- place_by_sortkey(tr, target);
- }
- );
- },
- "oncomplete": function() {
- progress_dialog.hide();
- if (any)
- window.print();
- else
- alert(dojo.byId("no_results").innerHTML);
- }
- }
- );
- }
-
function my_init() {
- var cgi = new CGI();
- var ses = (typeof ses == "function" ? ses() : 0) ||
+ cgi = new CGI();
+ authtoken = (typeof ses == "function" ? ses() : 0) ||
cgi.param("ses") || dojo.cookie("ses");
- var user = new openils.User({"authtoken": ses});
if (cgi.param("do") == "shelf_expired_holds") {
- do_shelf_expired_holds(user, cgi);
+ dojo.byId("clear_holds_launcher").onclick = function() {
+ if (confirm("Are you sure you're ready to clear the expired holds from the shelf?")) { /* XXX i18n */
+ do_clear_holds(cgi);
+ }
+ };
+ openils.Util.show("clear_holds_deck");
} else {
dojo.query("[only='shelf_expired_holds']").forEach(dojo.destroy);
- do_pull_list(user, cgi);
+ do_pull_list(cgi);
}
}
dojo.addOnLoad(my_init);
@@ -225,24 +64,34 @@
</head>
<body class='tundra'>
- <div dojoType="openils.widget.ProgressDialog" jsId="progress_dialog"></div>
+ <div style="width: 320px;"
+ dojoType="openils.widget.ProgressDialog"
+ jsId="progress_dialog"></div>
<div class="hide_me" id="no_results">No results</div>
+ <div class="hide_me" id="clear_holds_deck">
+ [ <a id="clear_holds_launcher"
+ href="javascript:void(0);">Clear expired holds</a> ]
+ <small><em id="clear_holds_set_label"></em></small>
+ </div>
<!-- START OF TEMPLATE SECTION -->
-
<table>
- <tbody id='target'>
+ <thead>
<tr>
<th only="shelf_expired_holds">Patron</th>
+ <th only="shelf_expired_holds">Action</th>
<th>Title</th>
<th>Author</th>
<th>Shelving Location</th>
<th>Call Number</th>
<th>Barcode</th>
</tr>
+ </thead>
+ <tbody id='target'>
</tbody>
<tbody id='template' class='hide_me'>
<tr>
<td only="shelf_expired_holds">${usr.display_name}</td>
+ <td only="shelf_expired_holds">${action}</td>
<td type='opac/slot-data' query='datafield[tag=245]'></td>
<td type='opac/slot-data' query='datafield[tag^=1]' limit='1'> </td>
<td>${current_copy.location.name}</td>
@@ -251,9 +100,6 @@
</tr>
</tbody>
</table>
-
<!-- END OF TEMPLATE SECTION -->
-
-
</body>
</html>
Added: trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.js
===================================================================
--- trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.js (rev 0)
+++ trunk/Open-ILS/web/opac/extras/circ/alt_holds_print.js 2010-10-07 22:37:45 UTC (rev 18230)
@@ -0,0 +1,202 @@
+dojo.require("dojo.cookie");
+dojo.require("dojox.xml.parser");
+dojo.require("openils.BibTemplate");
+dojo.require("openils.widget.ProgressDialog");
+
+var authtoken;
+var cgi;
+
+function do_pull_list() {
+ progress_dialog.show(true);
+
+ var any = false;
+
+ fieldmapper.standardRequest(
+ ['open-ils.circ','open-ils.circ.hold_pull_list.print.stream'],
+ { async : true,
+ params: [
+ authtoken, {
+ org_id : cgi.param('o'),
+ limit : cgi.param('limit'),
+ offset : cgi.param('offset'),
+ chunk_size : cgi.param('chunk_size'),
+ sort : sort_order
+ }
+ ],
+ onresponse : function (r) {
+ any = true;
+ dojo.forEach( openils.Util.readResponse(r), function (hold_fm) {
+
+ // hashify the hold
+ var hold = hold_fm.toHash(true);
+ hold.usr = hold_fm.usr().toHash(true);
+ hold.usr.card = hold_fm.usr().card().toHash(true);
+ hold.current_copy = hold_fm.current_copy().toHash(true);
+ hold.current_copy.location = hold_fm.current_copy().location().toHash(true);
+ hold.current_copy.call_number = hold_fm.current_copy().call_number().toHash(true);
+ hold.current_copy.call_number.record = hold_fm.current_copy().call_number().record().toHash(true);
+
+ // clone the template's html
+ var tr = dojo.clone(
+ dojo.query("tr", dojo.byId('template'))[0]
+ );
+ dojo.query("td:not([type])", tr).forEach(
+ function(td) {
+ td.innerHTML =
+ dojo.string.substitute(td.innerHTML, hold);
+ }
+ );
+
+ new openils.BibTemplate({
+ root : tr,
+ xml : dojox.xml.parser.parse(hold.current_copy.call_number.record.marc),
+ delay: false
+ });
+
+ dojo.place(tr, "target");
+ });
+ },
+ oncomplete : function () {
+ progress_dialog.hide();
+ if (any)
+ window.print();
+ else
+ alert(dojo.byId("no_results").innerHTML);
+ }
+ }
+ );
+}
+
+function place_by_sortkey(node, container) {
+ /*Don't use a forEach() or anything like that here. too slow.*/
+ var sortkey = dojo.attr(node, "sortkey");
+ for (var i = 0; i < container.childNodes.length; i++) {
+ var rover = container.childNodes[i];
+ if (rover.nodeType != 1) continue;
+ if (dojo.attr(rover, "sortkey") > sortkey) {
+ dojo.place(node, rover, "before");
+ return;
+ }
+ }
+ dojo.place(node, container, "last");
+}
+
+function hashify_fields(fields) {
+ var hold = {
+ "usr": {},
+ "current_copy": {
+ "barcode": fields.barcode,
+ "call_number": {
+ "label": fields.label,
+ "record": {"marc": fields.marc}
+ },
+ "location": {"name": fields.name}
+ }
+ };
+
+ if (fields.alias) {
+ hold.usr.display_name = fields.alias;
+ } else {
+ hold.usr.display_name = [
+ (fields.family_name ? fields.family_name : ""),
+ (fields.first_given_name ? fields.first_given_name : ""),
+ (fields.second_given_name ? fields.second_given_name : "")
+ ].join(" ");
+ }
+
+ ["first_given_name","second_given_name","family_name","alias"].forEach(
+ function(k) { hold.usr[k] = fields[k]; }
+ );
+
+ return hold;
+}
+
+function do_clear_holds() {
+ progress_dialog.show(true);
+
+ var launcher;
+ fieldmapper.standardRequest(
+ ["open-ils.circ", "open-ils.circ.hold.clear_shelf.process"], {
+ "async": true,
+ "params": [authtoken, cgi.param("o")],
+ "onresponse": function(r) {
+ if (r = openils.Util.readResponse(r)) {
+ if (r.cache_key) { /* complete */
+ launcher = dojo.byId("clear_holds_launcher");
+ launcher.innerHTML = "Re-fetch for Printing"; /* XXX i18n */
+ launcher.onclick =
+ function() { do_clear_holds_from_cache(r.cache_key); };
+ dojo.byId("clear_holds_set_label").innerHTML = r.cache_key;
+ } else if (r.maximum) {
+ progress_dialog.update(r);
+ }
+ }
+ },
+ "oncomplete": function() {
+ progress_dialog.hide();
+ if (launcher) launcher.onclick();
+ else alert(dojo.byId("no_results").innerHTML);
+ }
+ }
+ );
+}
+
+function do_clear_holds_from_cache(cache_key) {
+ progress_dialog.show(true);
+
+ var any = false;
+ var target = dojo.byId("target");
+ dojo.empty(target);
+ var template = dojo.query("tr", dojo.byId("template"))[0];
+ fieldmapper.standardRequest(
+ ["open-ils.circ",
+ "open-ils.circ.hold.clear_shelf.get_cache"], {
+ "async": true,
+ "params": [authtoken, cache_key, cgi.param("chunk_size")],
+ "onresponse": function(r) {
+ dojo.forEach(
+ openils.Util.readResponse(r),
+ function(resp) {
+ if (resp.maximum) {
+ progress_dialog.update(resp);
+ return;
+ }
+
+ var hold = hashify_fields(resp.hold_details);
+ hold.action = resp.action;
+
+ var tr = dojo.clone(template);
+ any = true;
+
+ dojo.query("td:not([type])", tr).forEach(
+ function(td) {
+ td.innerHTML =
+ dojo.string.substitute(td.innerHTML, hold);
+ }
+ );
+
+ new openils.BibTemplate({
+ "root": tr,
+ "xml": dojox.xml.parser.parse(
+ hold.current_copy.call_number.record.marc
+ ),
+ "delay": false
+ });
+
+ dojo.attr(tr, "sortkey", hold.usr.display_name);
+ place_by_sortkey(tr, target);
+ progress_dialog.update({"progress": 1});
+ }
+ );
+ },
+ "oncomplete": function() {
+ progress_dialog.hide();
+ if (any)
+ window.print();
+ else
+ alert(dojo.byId("no_results").innerHTML);
+ }
+ }
+ );
+}
+
More information about the open-ils-commits
mailing list