Merge pull request 'Filter call source' (#122) from filterSource into trunk
Reviewed-on: #122
This commit is contained in:
commit
2cf124cfc3
9 changed files with 129 additions and 51 deletions
|
@ -49,6 +49,25 @@
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<mat-form-field class="filterBox" subscriptSizing="dynamic">
|
||||||
|
<mat-label>Source Filter</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
name="sourceFilter"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
formControlName="sourceFilter"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="clearBtn"
|
||||||
|
*ngIf="form.controls['sourceFilter'].value"
|
||||||
|
matSuffix
|
||||||
|
mat-icon-button
|
||||||
|
(click)="form.controls['sourceFilter'].setValue('')"
|
||||||
|
>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-form-field>
|
||||||
<mat-form-field class="tagSelect" subscriptSizing="dynamic">
|
<mat-form-field class="tagSelect" subscriptSizing="dynamic">
|
||||||
<mat-label>Any Tags</mat-label>
|
<mat-label>Any Tags</mat-label>
|
||||||
<mat-select
|
<mat-select
|
||||||
|
@ -163,14 +182,26 @@
|
||||||
<a
|
<a
|
||||||
href="javascript:void(0)"
|
href="javascript:void(0)"
|
||||||
class="tgFilter"
|
class="tgFilter"
|
||||||
(click)="searchFilter(tgAlpha)"
|
(click)="searchTGFilter(tgAlpha)"
|
||||||
>{{ tgAlpha }}</a
|
>{{ tgAlpha }}</a
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="talker">
|
<ng-container matColumnDef="talker">
|
||||||
<th mat-header-cell *matHeaderCellDef>Source</th>
|
<th mat-header-cell *matHeaderCellDef>Source</th>
|
||||||
<td mat-cell *matCellDef="let call" [innerHTML]="call | talker"></td>
|
<td mat-cell *matCellDef="let call">
|
||||||
|
@let tlkAlias = call | talker;
|
||||||
|
@if (tlkAlias) {
|
||||||
|
<a
|
||||||
|
href="javascript:void(0)"
|
||||||
|
class="srcFilter"
|
||||||
|
(click)="searchSrcFilter(tlkAlias)"
|
||||||
|
>{{ tlkAlias }}</a
|
||||||
|
>
|
||||||
|
} @else {
|
||||||
|
—
|
||||||
|
}
|
||||||
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="duration">
|
<ng-container matColumnDef="duration">
|
||||||
<th mat-header-cell *matHeaderCellDef class="durationHdr">Duration</th>
|
<th mat-header-cell *matHeaderCellDef class="durationHdr">Duration</th>
|
||||||
|
|
|
@ -86,7 +86,7 @@ form {
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterBox {
|
.filterBox {
|
||||||
flex: 1 1 300px;
|
flex: 1 2 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.durationFilter {
|
.durationFilter {
|
||||||
|
@ -94,13 +94,14 @@ form {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagSelect {
|
.tagSelect {
|
||||||
flex: 1 1 250px;
|
flex: 1 1 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.in-incident {
|
.in-incident {
|
||||||
background-color: rgb(59, 0, 59);
|
background-color: rgb(59, 0, 59);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.tgFilter:hover {
|
a.tgFilter:hover,
|
||||||
|
a.srcFilter:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ export class CallsComponent {
|
||||||
start: new FormControl(this.lTime(new Date())),
|
start: new FormControl(this.lTime(new Date())),
|
||||||
end: new FormControl(null),
|
end: new FormControl(null),
|
||||||
filter: new FormControl(''),
|
filter: new FormControl(''),
|
||||||
|
sourceFilter: new FormControl(''),
|
||||||
duration: new FormControl(0),
|
duration: new FormControl(0),
|
||||||
tagsAny: new FormControl<string[]>([]),
|
tagsAny: new FormControl<string[]>([]),
|
||||||
tagsNot: new FormControl<string[]>([]),
|
tagsNot: new FormControl<string[]>([]),
|
||||||
|
@ -139,12 +140,18 @@ export class CallsComponent {
|
||||||
return numSelected === numRows;
|
return numSelected === numRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchFilter(filt: string | null) {
|
searchTGFilter(filt: string | null) {
|
||||||
if (filt) {
|
if (filt) {
|
||||||
this.form.controls['filter'].setValue(filt);
|
this.form.controls['filter'].setValue(filt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
searchSrcFilter(filt: string | null) {
|
||||||
|
if (filt) {
|
||||||
|
this.form.controls['sourceFilter'].setValue(filt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildParams(p: PageEvent, serverPage: number): CallsListParams {
|
buildParams(p: PageEvent, serverPage: number): CallsListParams {
|
||||||
const par: CallsListParams = {
|
const par: CallsListParams = {
|
||||||
start: new Date(this.form.controls['start'].value!),
|
start: new Date(this.form.controls['start'].value!),
|
||||||
|
@ -167,6 +174,10 @@ export class CallsComponent {
|
||||||
this.form.controls['filter'].value != ''
|
this.form.controls['filter'].value != ''
|
||||||
? this.form.controls['filter'].value
|
? this.form.controls['filter'].value
|
||||||
: null,
|
: null,
|
||||||
|
sourceFilter:
|
||||||
|
this.form.controls['sourceFilter'].value != ''
|
||||||
|
? this.form.controls['sourceFilter'].value
|
||||||
|
: null,
|
||||||
atLeastSeconds:
|
atLeastSeconds:
|
||||||
this.form.controls['duration'].value != null &&
|
this.form.controls['duration'].value != null &&
|
||||||
this.form.controls['duration'].value > 0
|
this.form.controls['duration'].value > 0
|
||||||
|
|
|
@ -33,12 +33,12 @@ export class DatePipe implements PipeTransform {
|
||||||
pure: true,
|
pure: true,
|
||||||
})
|
})
|
||||||
export class TalkerPipe implements PipeTransform {
|
export class TalkerPipe implements PipeTransform {
|
||||||
transform(call: CallRecord, args?: any): string {
|
transform(call: CallRecord, args?: any): string | null {
|
||||||
if (call.talkerAlias != null) {
|
if (call.talkerAlias != null) {
|
||||||
return call.talkerAlias;
|
return call.talkerAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '—';
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,6 +175,7 @@ export interface CallsListParams {
|
||||||
page: number;
|
page: number;
|
||||||
perPage: number;
|
perPage: number;
|
||||||
tgFilter: string | null;
|
tgFilter: string | null;
|
||||||
|
sourceFilter: string | null;
|
||||||
atLeastSeconds: number | null;
|
atLeastSeconds: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,18 @@
|
||||||
{{ call | talkgroup: "alpha" | async }}
|
{{ call | talkgroup: "alpha" | async }}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="talker">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Source</th>
|
||||||
|
<td mat-cell *matCellDef="let call">
|
||||||
|
@let tlkAlias = call | talker;
|
||||||
|
@if (tlkAlias) {
|
||||||
|
{{ tlkAlias }}
|
||||||
|
} @else {
|
||||||
|
—
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="duration">
|
<ng-container matColumnDef="duration">
|
||||||
<th mat-header-cell *matHeaderCellDef class="durationHdr">Duration</th>
|
<th mat-header-cell *matHeaderCellDef class="durationHdr">Duration</th>
|
||||||
<td mat-cell *matCellDef="let call" class="duration">
|
<td mat-cell *matCellDef="let call" class="duration">
|
||||||
|
|
|
@ -35,6 +35,7 @@ import {
|
||||||
TimePipe,
|
TimePipe,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
DownloadURLPipe,
|
DownloadURLPipe,
|
||||||
|
TalkerPipe,
|
||||||
} from '../../calls/calls.service';
|
} from '../../calls/calls.service';
|
||||||
import { CallPlayerComponent } from '../../calls/player/call-player/call-player.component';
|
import { CallPlayerComponent } from '../../calls/player/call-player/call-player.component';
|
||||||
import { FmtDatePipe } from '../incidents.component';
|
import { FmtDatePipe } from '../incidents.component';
|
||||||
|
@ -141,6 +142,7 @@ export class IncidentEditDialogComponent {
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
FixedPointPipe,
|
FixedPointPipe,
|
||||||
|
TalkerPipe,
|
||||||
TimePipe,
|
TimePipe,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
TalkgroupPipe,
|
TalkgroupPipe,
|
||||||
|
@ -169,6 +171,7 @@ export class IncidentComponent {
|
||||||
'system',
|
'system',
|
||||||
'group',
|
'group',
|
||||||
'talkgroup',
|
'talkgroup',
|
||||||
|
'talker',
|
||||||
'duration',
|
'duration',
|
||||||
];
|
];
|
||||||
callsResult = new MatTableDataSource<IncidentCall>();
|
callsResult = new MatTableDataSource<IncidentCall>();
|
||||||
|
|
|
@ -203,6 +203,7 @@ type CallsParams struct {
|
||||||
TagsAny []string `json:"tagsAny"`
|
TagsAny []string `json:"tagsAny"`
|
||||||
TagsNot []string `json:"tagsNot"`
|
TagsNot []string `json:"tagsNot"`
|
||||||
TGFilter *string `json:"tgFilter"`
|
TGFilter *string `json:"tgFilter"`
|
||||||
|
SourceFilter *string `json:"sourceFilter"`
|
||||||
AtLeastSeconds *float32 `json:"atLeastSeconds"`
|
AtLeastSeconds *float32 `json:"atLeastSeconds"`
|
||||||
UnknownTG bool `json:"unknownTG"`
|
UnknownTG bool `json:"unknownTG"`
|
||||||
}
|
}
|
||||||
|
@ -217,15 +218,16 @@ func (s *postgresStore) Calls(ctx context.Context, p CallsParams) (rows []databa
|
||||||
|
|
||||||
offset, perPage := p.Pagination.OffsetPerPage(100)
|
offset, perPage := p.Pagination.OffsetPerPage(100)
|
||||||
par := database.ListCallsPParams{
|
par := database.ListCallsPParams{
|
||||||
Start: p.Start.PGTypeTSTZ(),
|
Start: p.Start.PGTypeTSTZ(),
|
||||||
End: p.End.PGTypeTSTZ(),
|
End: p.End.PGTypeTSTZ(),
|
||||||
TagsAny: p.TagsAny,
|
TagsAny: p.TagsAny,
|
||||||
TagsNot: p.TagsNot,
|
TagsNot: p.TagsNot,
|
||||||
Offset: offset,
|
Offset: offset,
|
||||||
PerPage: perPage,
|
PerPage: perPage,
|
||||||
Direction: p.Direction.DirString(common.DirAsc),
|
Direction: p.Direction.DirString(common.DirAsc),
|
||||||
TGFilter: p.TGFilter,
|
TGFilter: p.TGFilter,
|
||||||
UnknownTG: p.UnknownTG,
|
SourceFilter: p.SourceFilter,
|
||||||
|
UnknownTG: p.UnknownTG,
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.AtLeastSeconds != nil {
|
if p.AtLeastSeconds != nil {
|
||||||
|
@ -241,13 +243,14 @@ func (s *postgresStore) Calls(ctx context.Context, p CallsParams) (rows []databa
|
||||||
txErr := db.InTx(ctx, func(db database.Store) error {
|
txErr := db.InTx(ctx, func(db database.Store) error {
|
||||||
var err error
|
var err error
|
||||||
count, err = db.ListCallsCount(ctx, database.ListCallsCountParams{
|
count, err = db.ListCallsCount(ctx, database.ListCallsCountParams{
|
||||||
Start: par.Start,
|
Start: par.Start,
|
||||||
End: par.End,
|
End: par.End,
|
||||||
TagsAny: par.TagsAny,
|
TagsAny: par.TagsAny,
|
||||||
TagsNot: par.TagsNot,
|
TagsNot: par.TagsNot,
|
||||||
TGFilter: par.TGFilter,
|
TGFilter: par.TGFilter,
|
||||||
LongerThan: par.LongerThan,
|
SourceFilter: p.SourceFilter,
|
||||||
UnknownTG: par.UnknownTG,
|
LongerThan: par.LongerThan,
|
||||||
|
UnknownTG: par.UnknownTG,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -317,22 +317,26 @@ CASE WHEN $4::TEXT[] IS NOT NULL THEN
|
||||||
tgs.name ILIKE '%' || $5 || '%' OR
|
tgs.name ILIKE '%' || $5 || '%' OR
|
||||||
tgs.alpha_tag ILIKE '%' || $5 || '%'
|
tgs.alpha_tag ILIKE '%' || $5 || '%'
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
(CASE WHEN $6::NUMERIC IS NOT NULL THEN (
|
(CASE WHEN $6::TEXT IS NOT NULL THEN (
|
||||||
c.duration > $6
|
c.talker_alias ILIKE '%' || $6 || '%'
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
(CASE WHEN $7::BOOLEAN = TRUE THEN (
|
(CASE WHEN $7::NUMERIC IS NOT NULL THEN (
|
||||||
|
c.duration > $7
|
||||||
|
) ELSE TRUE END) AND
|
||||||
|
(CASE WHEN $8::BOOLEAN = TRUE THEN (
|
||||||
tgs.tgid IS NULL
|
tgs.tgid IS NULL
|
||||||
) ELSE TRUE END)
|
) ELSE TRUE END)
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListCallsCountParams struct {
|
type ListCallsCountParams struct {
|
||||||
Start pgtype.Timestamptz `json:"start"`
|
Start pgtype.Timestamptz `json:"start"`
|
||||||
End pgtype.Timestamptz `json:"end"`
|
End pgtype.Timestamptz `json:"end"`
|
||||||
TagsAny []string `json:"tagsAny"`
|
TagsAny []string `json:"tagsAny"`
|
||||||
TagsNot []string `json:"tagsNot"`
|
TagsNot []string `json:"tagsNot"`
|
||||||
TGFilter *string `json:"tgFilter"`
|
TGFilter *string `json:"tgFilter"`
|
||||||
LongerThan pgtype.Numeric `json:"longerThan"`
|
SourceFilter *string `json:"sourceFilter"`
|
||||||
UnknownTG bool `json:"unknownTg"`
|
LongerThan pgtype.Numeric `json:"longerThan"`
|
||||||
|
UnknownTG bool `json:"unknownTg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) ListCallsCount(ctx context.Context, arg ListCallsCountParams) (int64, error) {
|
func (q *Queries) ListCallsCount(ctx context.Context, arg ListCallsCountParams) (int64, error) {
|
||||||
|
@ -342,6 +346,7 @@ func (q *Queries) ListCallsCount(ctx context.Context, arg ListCallsCountParams)
|
||||||
arg.TagsAny,
|
arg.TagsAny,
|
||||||
arg.TagsNot,
|
arg.TagsNot,
|
||||||
arg.TGFilter,
|
arg.TGFilter,
|
||||||
|
arg.SourceFilter,
|
||||||
arg.LongerThan,
|
arg.LongerThan,
|
||||||
arg.UnknownTG,
|
arg.UnknownTG,
|
||||||
)
|
)
|
||||||
|
@ -377,31 +382,35 @@ CASE WHEN $4::TEXT[] IS NOT NULL THEN
|
||||||
tgs.name ILIKE '%' || $5 || '%' OR
|
tgs.name ILIKE '%' || $5 || '%' OR
|
||||||
tgs.alpha_tag ILIKE '%' || $5 || '%'
|
tgs.alpha_tag ILIKE '%' || $5 || '%'
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
(CASE WHEN $6::NUMERIC IS NOT NULL THEN (
|
(CASE WHEN $6::TEXT IS NOT NULL THEN (
|
||||||
c.duration > $6
|
c.talker_alias ILIKE '%' || $6 || '%'
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
(CASE WHEN $7::BOOLEAN = TRUE THEN (
|
(CASE WHEN $7::NUMERIC IS NOT NULL THEN (
|
||||||
|
c.duration > $7
|
||||||
|
) ELSE TRUE END) AND
|
||||||
|
(CASE WHEN $8::BOOLEAN = TRUE THEN (
|
||||||
tgs.tgid IS NULL
|
tgs.tgid IS NULL
|
||||||
) ELSE TRUE END)
|
) ELSE TRUE END)
|
||||||
GROUP BY c.id, c.call_date
|
GROUP BY c.id, c.call_date
|
||||||
ORDER BY
|
ORDER BY
|
||||||
CASE WHEN $8::TEXT = 'asc' THEN c.call_date END ASC,
|
CASE WHEN $9::TEXT = 'asc' THEN c.call_date END ASC,
|
||||||
CASE WHEN $8 = 'desc' THEN c.call_date END DESC
|
CASE WHEN $9 = 'desc' THEN c.call_date END DESC
|
||||||
OFFSET $9 ROWS
|
OFFSET $10 ROWS
|
||||||
FETCH NEXT $10 ROWS ONLY
|
FETCH NEXT $11 ROWS ONLY
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListCallsPParams struct {
|
type ListCallsPParams struct {
|
||||||
Start pgtype.Timestamptz `json:"start"`
|
Start pgtype.Timestamptz `json:"start"`
|
||||||
End pgtype.Timestamptz `json:"end"`
|
End pgtype.Timestamptz `json:"end"`
|
||||||
TagsAny []string `json:"tagsAny"`
|
TagsAny []string `json:"tagsAny"`
|
||||||
TagsNot []string `json:"tagsNot"`
|
TagsNot []string `json:"tagsNot"`
|
||||||
TGFilter *string `json:"tgFilter"`
|
TGFilter *string `json:"tgFilter"`
|
||||||
LongerThan pgtype.Numeric `json:"longerThan"`
|
SourceFilter *string `json:"sourceFilter"`
|
||||||
UnknownTG bool `json:"unknownTg"`
|
LongerThan pgtype.Numeric `json:"longerThan"`
|
||||||
Direction string `json:"direction"`
|
UnknownTG bool `json:"unknownTg"`
|
||||||
Offset int32 `json:"offset"`
|
Direction string `json:"direction"`
|
||||||
PerPage int32 `json:"perPage"`
|
Offset int32 `json:"offset"`
|
||||||
|
PerPage int32 `json:"perPage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListCallsPRow struct {
|
type ListCallsPRow struct {
|
||||||
|
@ -421,6 +430,7 @@ func (q *Queries) ListCallsP(ctx context.Context, arg ListCallsPParams) ([]ListC
|
||||||
arg.TagsAny,
|
arg.TagsAny,
|
||||||
arg.TagsNot,
|
arg.TagsNot,
|
||||||
arg.TGFilter,
|
arg.TGFilter,
|
||||||
|
arg.SourceFilter,
|
||||||
arg.LongerThan,
|
arg.LongerThan,
|
||||||
arg.UnknownTG,
|
arg.UnknownTG,
|
||||||
arg.Direction,
|
arg.Direction,
|
||||||
|
|
|
@ -125,6 +125,9 @@ CASE WHEN sqlc.narg('tags_not')::TEXT[] IS NOT NULL THEN
|
||||||
tgs.name ILIKE '%' || @tg_filter || '%' OR
|
tgs.name ILIKE '%' || @tg_filter || '%' OR
|
||||||
tgs.alpha_tag ILIKE '%' || @tg_filter || '%'
|
tgs.alpha_tag ILIKE '%' || @tg_filter || '%'
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
|
(CASE WHEN sqlc.narg('source_filter')::TEXT IS NOT NULL THEN (
|
||||||
|
c.talker_alias ILIKE '%' || @source_filter || '%'
|
||||||
|
) ELSE TRUE END) AND
|
||||||
(CASE WHEN sqlc.narg('longer_than')::NUMERIC IS NOT NULL THEN (
|
(CASE WHEN sqlc.narg('longer_than')::NUMERIC IS NOT NULL THEN (
|
||||||
c.duration > @longer_than
|
c.duration > @longer_than
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
|
@ -158,6 +161,9 @@ CASE WHEN sqlc.narg('tags_not')::TEXT[] IS NOT NULL THEN
|
||||||
tgs.name ILIKE '%' || @tg_filter || '%' OR
|
tgs.name ILIKE '%' || @tg_filter || '%' OR
|
||||||
tgs.alpha_tag ILIKE '%' || @tg_filter || '%'
|
tgs.alpha_tag ILIKE '%' || @tg_filter || '%'
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
|
(CASE WHEN sqlc.narg('source_filter')::TEXT IS NOT NULL THEN (
|
||||||
|
c.talker_alias ILIKE '%' || @source_filter || '%'
|
||||||
|
) ELSE TRUE END) AND
|
||||||
(CASE WHEN sqlc.narg('longer_than')::NUMERIC IS NOT NULL THEN (
|
(CASE WHEN sqlc.narg('longer_than')::NUMERIC IS NOT NULL THEN (
|
||||||
c.duration > @longer_than
|
c.duration > @longer_than
|
||||||
) ELSE TRUE END) AND
|
) ELSE TRUE END) AND
|
||||||
|
|
Loading…
Add table
Reference in a new issue