[#75] Device module inventory report / search

This commit is contained in:
Eric A. Miller
2014-02-15 22:07:55 -05:00
parent 8cee58c88e
commit ad8bcc3dfc
9 changed files with 419 additions and 2 deletions

View File

@@ -0,0 +1,107 @@
package App::Netdisco::DB::ResultSet::DeviceModule;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings FATAL => 'all';
=head1 ADDITIONAL METHODS
=head2 search_by_field( \%cond, \%attrs? )
This variant of the standard C<search()> method returns a ResultSet of Device
Module entries. It is written to support web forms which accept fields that
match and locate Device Modules in the database.
The hashref parameter should contain fields from the Device Module table
which will be intelligently used in a search query.
In addition, you can provide the key C<matchall> which, given a True or False
value, controls whether fields must all match or whether any can match, to
select a row.
Supported keys:
=over 4
=item matchall
If a True value, fields must all match to return a given row of the Device
table, otherwise any field matching will cause the row to be included in
results.
=item description
Can match the C<description> field as a substring.
=item name
Can match the C<name> field as a substring.
=item type
Can match the C<type> field as a substring.
=item model
Can match the C<model> field as a substring.
=item serial
Can match the C<serial> field as a substring.
=item class
Will match exactly the C<class> field.
=item ips
List of Device IPs containing modules.
=back
=cut
sub search_by_field {
my ( $rs, $p, $attrs ) = @_;
die "condition parameter to search_by_field must be hashref\n"
if ref {} ne ref $p
or 0 == scalar keys %$p;
my $op = $p->{matchall} ? '-and' : '-or';
return $rs->search_rs( {}, $attrs )->search(
{ $op => [
( $p->{description}
? ( 'me.description' =>
{ '-ilike' => "\%$p->{description}\%" } )
: ()
),
( $p->{name}
? ( 'me.name' => { '-ilike' => "\%$p->{name}\%" } )
: ()
),
( $p->{type}
? ( 'me.type' => { '-ilike' => "\%$p->{type}\%" } )
: ()
),
( $p->{model}
? ( 'me.model' => { '-ilike' => "\%$p->{model}\%" } )
: ()
),
( $p->{serial}
? ( 'me.serial' => { '-ilike' => "\%$p->{serial}\%" } )
: ()
),
( $p->{class}
? ( 'me.class' => { '-in' => $p->{class} } )
: ()
),
( $p->{ips} ? ( 'me.ip' => { '-in' => $p->{ips} } ) : () ),
],
}
);
}
1;

View File

@@ -0,0 +1,109 @@
package App::Netdisco::Web::Plugin::Report::ModuleInventory;
use Dancer ':syntax';
use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin;
use List::MoreUtils ();
register_report(
{ category => 'Device',
tag => 'moduleinventory',
label => 'Module Inventory',
provides_csv => 1,
}
);
hook 'before' => sub {
# view settings
var('module_options' => [
{ name => 'matchall',
label => 'Match All Options',
default => 'on'
},
]
);
return
unless (
request->path eq uri_for('/report/moduleinventory')->path
or index( request->path,
uri_for('/ajax/content/report/moduleinventory')->path ) == 0
);
params->{'limit'} ||= 1024;
foreach my $col ( @{ var('module_options') } ) {
next unless $col->{default} eq 'on';
params->{ $col->{name} } = 'checked';
}
};
hook 'before_template' => sub {
my $tokens = shift;
return
unless (
request->path eq uri_for('/report/moduleinventory')->path
or index( request->path,
uri_for('/ajax/content/report/moduleinventory')->path ) == 0
);
# used in the search sidebar template to set selected items
foreach my $opt (qw/class/) {
my $p = (
ref [] eq ref param($opt)
? param($opt)
: ( param($opt) ? [ param($opt) ] : [] )
);
$tokens->{"${opt}_lkp"} = { map { $_ => 1 } @$p };
}
};
get '/ajax/content/report/moduleinventory' => require_login sub {
my $has_opt = List::MoreUtils::any { param($_) }
qw/device description name type model serial class/;
my $rs = schema('netdisco')->resultset('DeviceModule');
if ($has_opt) {
if ( param('device') ) {
my @ips = schema('netdisco')->resultset('Device')
->search_fuzzy( param('device') )->get_column('ip')->all;
params->{'ips'} = \@ips;
}
$rs = $rs->search_by_field( scalar params )->prefetch('device')
->limit( param('limit') )->hri;
}
else {
$rs = $rs->search(
{},
{ select => [ 'class', { count => 'class' } ],
as => [qw/ class count /],
group_by => [qw/ class /]
}
)->order_by( { -desc => 'count' } )->hri;
}
return unless $rs->has_rows;
if ( request->is_ajax ) {
template 'ajax/report/moduleinventory.tt', { results => $rs, },
{ layout => undef };
}
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/moduleinventory_csv.tt', { results => $rs, },
{ layout => undef };
}
};
1;

View File

@@ -12,6 +12,8 @@ get '/report/*' => require_login sub {
= [
schema('netdisco')->resultset('NodeNbt')->get_distinct_col('domain')
];
my $class_list = [ schema('netdisco')->resultset('DeviceModule')
->get_distinct_col('class') ];
# trick the ajax into working as if this were a tabbed page
params->{tab} = $tag;
@@ -21,6 +23,7 @@ get '/report/*' => require_login sub {
{
report => setting('_reports')->{$tag},
domain_list => $domain_list,
class_list => $class_list,
};
};