these things:
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Thu, 29 Mar 2012 17:48:13 +0000 (13:48 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Thu, 29 Mar 2012 17:48:13 +0000 (13:48 -0400)
    1) actually use map key, refetching if it has expired
    2) make csv output actually work
    3) make htmlish output actually work, mostly, but still has
        unwanted '0' after the end of the html output?

Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/src/extras/ils_events.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Fielder.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/FlatFielder.pm
Open-ILS/src/templates/conify/flattener_test.tt2
Open-ILS/web/js/dojo/openils/FlattenerStore.js

index 59ff421..9708e9e 100644 (file)
        <event code='3' textcode='NO_CHANGE'>
                <desc xml:lang="en-US">No change occurred</desc>
        </event>
+
+    <event code='4' textcode='CACHE_MISS'>
+        <desc xml:lang="en-US">A cached object could not be retrieved by the given reference.</desc>
+    </event>
+
        <event code='1000' textcode='LOGIN_FAILED'>
                <desc xml:lang="en-US">User login failed</desc>
        </event>
index 3d9ae13..343c285 100644 (file)
@@ -172,7 +172,7 @@ sub register_map {
 __PACKAGE__->register_method(
     method          => 'register_map',
     api_name        => 'open-ils.fielder.flattened_search.prepare',
-    argc            => 2,
+    argc            => 3,
     signature       => {
         params => [
             {name => "auth", type => "string", desc => "auth token"},
@@ -199,8 +199,10 @@ sub execute_registered_flattened_search {
 
     my $e = new_editor(authtoken => $auth);
     return $e->event unless $e->checkauth;
+    $e->disconnect;
 
-    my $blob = $cache->get_cache( $key );
+    my $blob = $cache->get_cache( $key ) or
+        return new OpenILS::Event('CACHE_MISS');
 
     flattened_search( $self, $conn, $auth, $blob->{hint}, $blob->{map}, @_ )
         if (ref($blob) and $blob->{hint} and $blob->{map});
index 549b46b..4cb25cc 100644 (file)
@@ -5,7 +5,7 @@ use warnings;
 
 use Apache2::Log;
 use Apache2::Const -compile => qw(
-    OK HTTP_NOT_ACCEPTABLE HTTP_INTERNAL_SERVER_ERROR :log
+    OK HTTP_NOT_ACCEPTABLE HTTP_PAYMENT_REQUIRED HTTP_INTERNAL_SERVER_ERROR :log
 );
 use XML::LibXML;
 use XML::LibXSLT;
@@ -16,6 +16,9 @@ use OpenSRF::Utils::JSON;
 use OpenSRF::AppSession;
 use OpenSRF::Utils::SettingsClient;
 
+use OpenILS::Application::AppUtils;
+my $U = 'OpenILS::Application::AppUtils';
+
 
 my $_parser = new XML::LibXML;
 my $_xslt = new XML::LibXSLT;
@@ -31,7 +34,7 @@ my $_xslt = new XML::LibXSLT;
 sub html_ish_output {
     my ($r, $args, $xslt) = @_;
     $args->{'stylesheet'} =
-        OpenSRF::Utils::SettingsClient->new->config_value(dirs => 'xsl') . $xslt;
+        OpenSRF::Utils::SettingsClient->new->config_value(dirs => 'xsl') . '/' . $xslt;
     print data_to_xml($args);
     return Apache2::Const::OK;
 }
@@ -42,15 +45,17 @@ my $_output_handler_dispatch = {
         "code" => sub {
             my ($r, $args) = @_;
             $r->headers_out->set("Content-Disposition" => "attachment; filename=FlatSearch.csv");
-            $r->content_type('text/csv; name=FlatSearch.csv; charset=utf-8');
-            return data_to_csv( $args );
+            $r->content_type('text/csv; charset=utf-8');
+            print_data_as_csv($args, \*STDOUT);
+            return Apache2::Const::OK;
         }
     },
     "text/html" => {
         "prio" => 0,
         "code" => sub {
             $_[0]->content_type("text/html; charset=utf-8");
-            return html_ish_output( @_, 'FlatFielder2HTML.xsl' );
+            print html_ish_output( @_, 'FlatFielder2HTML.xsl' );
+            return Apache2::Const::OK;
         }
     },
     "application/xml" => {
@@ -132,25 +137,19 @@ sub data_to_xml {
     return $dom->toString();
 }
 
-sub data_to_csv {
-    my ($args) = @_;
-
-    my @keys = sort { $a cmp $b } keys %{ $$args{data}[0] };
-    return if (!@keys);
+sub print_data_as_csv {
+    my ($args, $fh) = @_;
 
-    my $output = quote_for_csv(@keys);
+    my @keys = sort keys %{ $$args{data}[0] };
+    return unless @keys;
 
-    for my $i (@{$$args{data}}) {
-        $output = quote_for_csv(
-            map { $$args{data}[$i]{$_} } @keys
-        );
-    }
+    my $csv = new Text::CSV({ always_quote => 1, eol => "\r\n" });
 
-    return $output;
-}
+    $csv->print($fh, \@keys);
 
-sub quote_for_csv {
-    return '"' . join('","', map { s/"/""/g } @_ ) . "\"\015\012";
+    for my $row (@{$$args{data}}) {
+        $csv->print($fh, [map { $row->{$_} } @keys]);
+    }
 }
 
 sub data_to_json {
@@ -223,6 +222,13 @@ sub handler {
             'open-ils.fielder.flattened_search.execute.atomic',
             @args{qw/auth key where slo/}
         )->gather(1);
+
+        if (ref $args{data} and $args{data}[0] and
+            $U->event_equals($args{data}[0], 'CACHE_MISS')) {
+
+            # You have to pay the cache! I kill me.
+            return Apache2::Const::HTTP_PAYMENT_REQUIRED;
+        }
     }
 
     return output_handler( $r, \%args );
index d8944a1..1011e44 100644 (file)
@@ -38,7 +38,7 @@
         query="{'owner': 'BR1'}">
         <thead>
             <tr>
-                <th field="barcode" fpath="barcode">Barcode</th>
+                <th field="barcode" fpath="barcode" ffilter="true">Barcode</th>
                 <th field="owner" fpath="owner.shortname" ffilter="true">Circulation Library</th>
                 <th field="resource_type" fpath="type.name">Resource type</th>
             </tr>
index 8b0a301..ad8ec29 100644 (file)
@@ -3,6 +3,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
 
     dojo.provide("openils.FlattenerStore");
 
+    dojo.require("DojoSRF");
     dojo.require("openils.User");
     dojo.require("openils.Util");
 
@@ -62,7 +63,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
         },
 
         "_prepare_flattener_params": function(req) {
-            var content = {
+            var params = {
                 "hint": this.fmClass,
                 "ses": openils.User.authtoken
             };
@@ -73,7 +74,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                 var where = {};
                 where[this.fmIdentifier] = req.identity;
 
-                content.where = dojo.toJson(where);
+                params.where = dojo.toJson(where);
             } else {
                 var limit = (!isNaN(req.count) && req.count != Infinity) ?
                     req.count : this.limit;
@@ -81,7 +82,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                     req.start : this.offset;
 
                 dojo.mixin(
-                    content, {
+                    params, {
                         "where": dojo.toJson(req.query),
                         "slo": dojo.toJson({
                             "sort": this._prepare_sort(req.sort),
@@ -93,12 +94,15 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
             }
 
             if (this.mapKey) { /* XXX TODO, get a map key */
-                content.key = this.mapKey;
+                params.key = this.mapKey;
             } else {
-                content.map = dojo.toJson(this.mapClause);
+                params.map = dojo.toJson(this.mapClause);
             }
 
-            return content;
+            for (var key in params)
+                console.debug("flattener param " + key + " -> " + params[key]);
+
+            return params;
         },
 
         "_display_attributes": function() {
@@ -108,6 +112,19 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                 function(key) { return self.mapClause[key].display; }
             );
         },
+
+        "_get_map_key": function() {
+            console.debug("mapClause: " + dojo.toJson(this.mapClause));
+            this.mapKey = fieldmapper.standardRequest(
+                ["open-ils.fielder",
+                    "open-ils.fielder.flattened_search.prepare"], {
+                    "params": [openils.User.authtoken, this.fmClass,
+                        this.mapClause],
+                    "async": false
+                }
+            );
+        },
+
         /* *** Begin dojo.data.api.Read methods *** */
 
         "getValue": function(
@@ -226,10 +243,20 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
             //  the one we provide does nothing but issue an alert().
 
             console.info("fetch(" + dojo.toJson(req) + ")");
+            var self = this;
+            var callback_scope = req.scope || dojo.global;
 
-            // this._current_items={}; /* I'm pretty sure we don't want this */
+            if (!this.mapKey) {
+                try {
+                    this._get_map_key();
+                } catch (E) {
+                    if (req.onError)
+                        req.onError.call(callback_scope, E);
+                    else
+                        throw E;
+                }
+            }
 
-            var callback_scope = req.scope || dojo.global;
             var post_params = this._prepare_flattener_params(req);
 
             if (!post_params) {
@@ -238,11 +265,12 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                 return;
             }
 
-            var self = this;
             var process_fetch = function(obj, when) {
                 if (when < self._last_fetch) /* Stale response. Discard. */
                     return;
 
+                self._retried_map_key_already = false;
+
                 /* The following is apparently the "right" way to call onBegin,
                  * and is very necessary (at least in Dojo 1.3.3) to get
                  * the Grid's fetch-more-when-I-need-it logic to work
@@ -294,7 +322,19 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                 "sync": false,
                 "preventCache": true,
                 "headers": {"Accept": "application/json"},
-                "load": function(obj) { process_fetch(obj, fetch_time); }
+                "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) {
+                            console.error("Server won't cache flattener map?");
+                        } else {
+                            self._retried_map_key_already = true;
+                            delete self.mapKey;
+                            return self.fetch(req);
+                        }
+                    }
+                }
             });
 
             /* as for onError: what to do? */