Support for data munging in User Reports

This commit is contained in:
Oliver Gorwits
2014-03-02 23:29:09 +00:00
parent 3ff9f3c587
commit 32771b5ada
7 changed files with 53 additions and 19 deletions

View File

@@ -5,6 +5,7 @@
* Wildcard support on Device Port MAC Search * Wildcard support on Device Port MAC Search
* Wildcard support on Node name/MAC Search * Wildcard support on Node name/MAC Search
* User Reports appear in "My Reports" menu if no category is given * User Reports appear in "My Reports" menu if no category is given
* (beta) Support for data munging in User Reports
* Permit clipboard copy of device IP from Job Queue rows * Permit clipboard copy of device IP from Job Queue rows
* Failed Job Device IPs link to Discover form with pre-filled Discover IP * Failed Job Device IPs link to Discover form with pre-filled Discover IP
* Unknown device page has pre-filled Discover IP * Unknown device page has pre-filled Discover IP

View File

@@ -34,6 +34,7 @@ requires 'Net::LDAP' => 0;
requires 'Net::MAC' => 2.103622; requires 'Net::MAC' => 2.103622;
requires 'Net::NBName' => 0.26; requires 'Net::NBName' => 0.26;
requires 'NetAddr::IP' => 4.068; requires 'NetAddr::IP' => 4.068;
requires 'Opcode' => 1.07;
requires 'Path::Class' => 0.32; requires 'Path::Class' => 0.32;
requires 'Plack' => 1.0023; requires 'Plack' => 1.0023;
requires 'Plack::Middleware::Expires' => 0.03; requires 'Plack::Middleware::Expires' => 0.03;

View File

@@ -243,8 +243,14 @@ code or HTML templates. For example:
ORDER BY name ORDER BY name
Each key of the C<reports> configuration is an alias for the report, and Each key of the C<reports> configuration is an alias for the report, and
becomes part of the web path. Within the tree you must provide each of the becomes part of the web path.
keys shown:
You can munge the data retrieved from the database by placing a Perl script
with the same name as the C<reports> key into the C<site_plugins> directory of
Netdisco's home area. The script can access C<$config> for its configuration
and C<@data> for the retrieved data. It should return a list of munged data.
Within the tree you can provide each of the keys below:
=head4 C<label> =head4 C<label>
@@ -258,8 +264,9 @@ heading.
=head4 C<query> =head4 C<query>
SQL which returns the data. Make sure that the columns are named the same as SQL which returns the data. Make sure that the columns are named the same as
the keys of the C<columns> configuration. Note the way the SQL is specified in the keys of the C<columns> or C<query_columns> configuration. Note the way the
the example above, using the pipe symbol and then indenting the query text. SQL is specified in the example above, using the pipe symbol and then
indenting the query text.
=head4 C<category> (optional) =head4 C<category> (optional)
@@ -267,6 +274,13 @@ Section of the Reports menu where this report will appear. See
L<WritingPlugins|App::Netdisco::Manual::WritingPlugins> for the full list. L<WritingPlugins|App::Netdisco::Manual::WritingPlugins> for the full list.
If not supplied, reports appear in a I<My Reports> category. If not supplied, reports appear in a I<My Reports> category.
=head4 C<query_columns> (optional)
If supplying code to munge the data, the columns returned from your database
C<query> may not be the same as those in the web report. Set this to a list of
the columns in C<query>. The C<columns> setting will then be used for the web
report.
=head3 C<jobqueue_refresh> =head3 C<jobqueue_refresh>
Value: Integer Number. Default: 5. Value: Integer Number. Default: 5.

View File

@@ -6,9 +6,10 @@ use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
use Path::Class 'file';
use Safe;
# in case user did not set use vars qw/$config @data/;
config->{reports} ||= {};
foreach my $r (keys %{setting('reports')}) { foreach my $r (keys %{setting('reports')}) {
my $report = setting('reports')->{$r}; my $report = setting('reports')->{$r};
@@ -23,28 +24,44 @@ foreach my $r (keys %{setting('reports')}) {
get "/ajax/content/report/$r" => require_login sub { get "/ajax/content/report/$r" => require_login sub {
my $rs = schema('netdisco')->resultset('Virtual::GenericReport')->result_source; my $rs = schema('netdisco')->resultset('Virtual::GenericReport')->result_source;
# this should be done by creating a new Virtual Result class on the fly # TODO: this should be done by creating a new Virtual Result class on
# (package...) and then calling DBIC register_class on it. # the fly (package...) and then calling DBIC register_class on it.
$rs->view_definition($report->{query}); $rs->view_definition($report->{query});
$rs->remove_columns($rs->columns); $rs->remove_columns($rs->columns);
$rs->add_columns(map {keys %{$_}} @{$report->{columns}}); $rs->add_columns( exists $report->{query_columns}
? @{ $report->{query_columns} }
: (map {keys %{$_}} @{$report->{columns}})
);
my $set = schema('netdisco')->resultset('Virtual::GenericReport'); my $set = schema('netdisco')->resultset('Virtual::GenericReport')
return unless $set->count; ->search(undef, {result_class => 'DBIx::Class::ResultClass::HashRefInflator'});
@data = $set->all;
# Data Munging support...
my $compartment = Safe->new;
$config = setting('reports')->{$r};
$compartment->share(qw/$config @data/);
$compartment->permit_only(qw/:default sort/);
my $munger = file(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'site_plugins', $r)->stringify;
my @results = ((-f $munger) ? $compartment->rdo( $munger ) : @data);
return if $@ or (0 == scalar @results);
if (request->is_ajax) { if (request->is_ajax) {
template 'ajax/report/generic_report.tt', template 'ajax/report/generic_report.tt',
{ results => $set, { results => \@results,
headings => [map {values %{$_}} @{$report->{columns}}], headings => [map {values %{$_}} @{$report->{columns}}],
columns => [$rs->columns] }, columns => [map {keys %{$_}} @{$report->{columns}}] },
{ layout => undef }; { layout => undef };
} }
else { else {
header( 'Content-Type' => 'text/comma-separated-values' ); header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/generic_report_csv.tt', template 'ajax/report/generic_report_csv.tt',
{ results => $set, { results => \@results,
headings => [map {values %{$_}} @{$report->{columns}}], headings => [map {values %{$_}} @{$report->{columns}}],
columns => [$rs->columns] }, columns => [map {keys %{$_}} @{$report->{columns}}] },
{ layout => undef }; { layout => undef };
} }
}; };

View File

@@ -78,6 +78,7 @@ web_plugins:
extra_web_plugins: [] extra_web_plugins: []
jobqueue_refresh: 10 jobqueue_refresh: 10
safe_password_store: true safe_password_store: true
reports: {}
# ------------- # -------------
# NETDISCO CORE # NETDISCO CORE

View File

@@ -7,10 +7,10 @@
</tr> </tr>
</thead> </thead>
</tbody> </tbody>
[% WHILE (row = results.next) %] [% FOREACH row IN results %]
<tr> <tr>
[% FOREACH col IN columns %] [% FOREACH col IN columns %]
<td>[% row.get_column(col) | html_entity %]</td> <td>[% row.item(col) | html_entity %]</td>
[% END %] [% END %]
</tr> </tr>
[% END %] [% END %]

View File

@@ -1,10 +1,10 @@
[% USE CSV %] [% USE CSV %]
[% CSV.dump(headings) %] [% CSV.dump(headings) %]
[% FOREACH row IN results.all %] [% FOREACH row IN results %]
[% mylist = [] %] [% mylist = [] %]
[% FOREACH col IN columns %] [% FOREACH col IN columns %]
[% mylist.push(row.get_column(col)) %] [% mylist.push(row.item(col)) %]
[% END %] [% END %]
[% CSV.dump(mylist) %] [% CSV.dump(mylist) %]