Skip to content

Commit

Permalink
Support to calculate numeric custom fields in search charts
Browse files Browse the repository at this point in the history
  • Loading branch information
sunnavy committed Dec 5, 2023
1 parent 295e37c commit d067f08
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 5 deletions.
85 changes: 81 additions & 4 deletions lib/RT/Report.pm
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,41 @@ our %STATISTICS_META = (
},
Display => 'DurationAsString',
},
CustomFieldNumericRange => {
Function => sub {
my $self = shift;
my $function = shift;
my $id = shift;
my $cf = RT::CustomField->new( RT->SystemUser );
$cf->Load($id);
my ($ocfv_alias) = $self->_CustomFieldJoin( $id, $cf );
my $cast = $self->_CastToDecimal('Content');
my $precision = $self->_CustomFieldNumericPrecision($cf) // 3;
return (
FUNCTION => $function eq 'AVG' ? "ROUND($function($cast), $precision)" : "$function($cast)",
ALIAS => $ocfv_alias,
);
},
},
CustomFieldNumericRangeAll => {
SubValues => sub { return ( 'Minimum', 'Average', 'Maximum', 'Total' ) },
Function => sub {
my $self = shift;
my $id = shift;
my $cf = RT::CustomField->new( RT->SystemUser );
$cf->Load($id);
my ($ocfv_alias) = $self->_CustomFieldJoin( $id, $cf );
my $cast = $self->_CastToDecimal('Content');
my $precision = $self->_CustomFieldNumericPrecision($cf) // 3;

return (
Minimum => { FUNCTION => "MIN($cast)", ALIAS => $ocfv_alias },
Average => { FUNCTION => "ROUND(AVG($cast), $precision)", ALIAS => $ocfv_alias },
Maximum => { FUNCTION => "MAX($cast)", ALIAS => $ocfv_alias },
Total => { FUNCTION => "SUM($cast)", ALIAS => $ocfv_alias },
);
},
},
);

sub Groupings {
Expand Down Expand Up @@ -477,8 +512,9 @@ sub IsValidGrouping {
}

sub Statistics {
my $self = shift;
return map { ref($_)? $_->[0] : $_ } $self->_Statistics;
my $self = shift;
my @items = $self->_Statistics;
return @items, $self->_NumericCustomFields(@_);
}

sub Label {
Expand Down Expand Up @@ -574,8 +610,7 @@ sub SetupGroupings {
push @{ $res{'Groups'} }, $group_by->{'NAME'};
}

my %statistics = $self->_Statistics;

my %statistics = $self->Statistics(%args);
my @function = grep defined && length,
ref( $args{'Function'} )? @{ $args{'Function'} } : ($args{'Function'});
push @function, 'COUNT' unless @function;
Expand Down Expand Up @@ -1264,6 +1299,45 @@ sub _SetupCustomDateRanges {
return 1;
}

sub _NumericCustomFields {
my $self = shift;
my %args = @_;
my $custom_fields = RT::CustomFields->new( $self->CurrentUser );
$custom_fields->LimitToLookupType( $self->RecordClass->CustomFieldLookupType );
$custom_fields->LimitToObjectId(0);

if ( $args{'Query'} ) {
require RT::Interface::Web::QueryBuilder::Tree;
my $tree = RT::Interface::Web::QueryBuilder::Tree->new('AND');
$tree->ParseSQL( Query => $args{'Query'}, CurrentUser => $self->CurrentUser, Class => ref $self );
my $queues = $tree->GetReferencedQueues( CurrentUser => $self->CurrentUser );
foreach my $id ( keys %$queues ) {
my $queue = RT::Queue->new( $self->CurrentUser );
$queue->Load($id);
next unless $queue->id;
$custom_fields->SetContextObject($queue) if keys %$queues == 1;
$custom_fields->LimitToObjectId( $queue->id );
}
}

my @items;
while ( my $custom_field = $custom_fields->Next ) {
next unless $custom_field->IsNumeric && $custom_field->SingleValue;
my $id = $custom_field->Id;
my $name = $custom_field->Name;

push @items,
(
"ALL(CF.$id)" => [ "Summary of $name", 'CustomFieldNumericRangeAll', $id ],
"SUM(CF.$id)" => [ "Total $name", 'CustomFieldNumericRange', 'SUM', $id ],
"AVG(CF.$id)" => [ "Average $name", 'CustomFieldNumericRange', 'AVG', $id ],
"MIN(CF.$id)" => [ "Minimum $name", 'CustomFieldNumericRange', 'MIN', $id ],
"MAX(CF.$id)" => [ "Maximum $name", 'CustomFieldNumericRange', 'MAX', $id ],
);
}
return @items;
}

sub _GroupingType {
my $self = shift;
my $key = shift or return;
Expand Down Expand Up @@ -1339,6 +1413,9 @@ sub NewItem {
sub _RoleGroupClass { die "should be subclassed" }
sub _SingularClass { die "should be subclassed" }

# Precision can be customized by overriding this method
# it'll be called as $self->_CustomFieldNumericPrecision($cf)
sub _CustomFieldNumericPrecision { 3 }

RT::Base->_ImportOverlays();

Expand Down
5 changes: 4 additions & 1 deletion share/html/Search/Elements/SelectChartFunction
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
my $in_optgroup = "";
while ( my ($value, $display) = splice @functions, 0, 2 ) {
my $optgroup = $value =~ /\((.+)\)$/ ? $1 : $display;
$optgroup = 'Custom field' if $optgroup =~ /^CF\./;
if ($in_optgroup ne $optgroup) {
$m->out("</optgroup>\n") if $in_optgroup;

Expand All @@ -73,9 +74,11 @@ $Name => 'ChartFunction'
$Default => 'COUNT'
$ShowEmpty => 0
$Class => $Class
$Query => ''
</%ARGS>
<%INIT>
my $report_class = ( $Class || 'RT::Tickets' )->ReportClass;
RT::StaticUtil::RequireModule($report_class) or Abort( loc("Couldn't load [_1]", $report_class) );
my @functions = $report_class->Statistics;
my @functions
= map { ref($_) ? $_->[0] : $_ } $report_class->new( $session{CurrentUser} )->Statistics( Query => $Query );
</%INIT>

0 comments on commit d067f08

Please sign in to comment.