Skip to content

Commit

Permalink
Agent camera masks and ui themes (#110)
Browse files Browse the repository at this point in the history
* websocket listener

* ws configurable

* show agent status from websocket on frontend

* fix for linux decimals

* snackbar support

* agent status on page updates in real time

* correct field saving

* add autofocus commands, more agent events

* added image mask saving to agent

* some fixes

* get it saving on the handler properly

* finish enable/disable logic, fix config sending

* more tweaks and fixes

* add pagination on mask page

* add some error handling on image load

* proper resolution

* correct coordinates

* standalone components

* theming, login, etc

* angular 17

* fancy glows, multi theme

* some css fixes

* reorg

* send the image too

* only send when configured to

* not for all

* schedule agent scrape

* fix tests

* open to plate directly from web push alert

* open url from chrome

* no dev mode

* open on windows

* linting

* tests work

* get fe tests running on gha

* working directory

* test separate

* backend tests

* more test work

* no lower bound
  • Loading branch information
mlapaglia authored Nov 23, 2023
1 parent 6081aad commit c169956
Show file tree
Hide file tree
Showing 260 changed files with 15,501 additions and 6,792 deletions.
59 changes: 52 additions & 7 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,59 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run tests
- name: Run build
run: docker build . --file ./OpenAlprWebhookProcessor/Dockerfile

back-end-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.x

- name: Restore Dependencies
run: dotnet restore

- name: Build
run: dotnet build --configuration Release --no-restore

- name: Test
run: dotnet test --configuration Release --no-build --collect:"XPlat Code Coverage" --results-directory ./coverage

- name: Code Coverage Report
uses: irongut/[email protected]
with:
filename: coverage/**/coverage.cobertura.xml
badge: true
fail_below_min: true
format: markdown
hide_branch_rate: false
hide_complexity: true
indicators: true
output: both
thresholds: '0 80'

- name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
with:
recreate: true
path: code-coverage-results.md

front-end-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run frontend tests
working-directory: ./OpenAlprWebhookProcessor/angularapp
run: |
if [ -f docker-compose.test.yml ]; then
docker-compose --file docker-compose.test.yml build
docker-compose --file docker-compose.test.yml run sut
else
docker build . --file ./OpenAlprWebhookProcessor/Dockerfile
fi
npm ci
npm run test:prod
npm run lint
windows-build-push:
needs: build
Expand Down
10 changes: 8 additions & 2 deletions OpenAlprWebhookProcessor.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30804.86
# Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{350E0804-C372-49AB-B670-75E037878FAF}"
ProjectSection(SolutionItems) = preProject
Expand All @@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAlprWebhookProcessor", "OpenAlprWebhookProcessor\OpenAlprWebhookProcessor.csproj", "{7C3E9804-0A13-4060-B5FD-91A613647146}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{DD123D3E-8A09-4104-AF20-D5FBFAA6F52E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -20,6 +22,10 @@ Global
{7C3E9804-0A13-4060-B5FD-91A613647146}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C3E9804-0A13-4060-B5FD-91A613647146}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C3E9804-0A13-4060-B5FD-91A613647146}.Release|Any CPU.Build.0 = Release|Any CPU
{DD123D3E-8A09-4104-AF20-D5FBFAA6F52E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD123D3E-8A09-4104-AF20-D5FBFAA6F52E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD123D3E-8A09-4104-AF20-D5FBFAA6F52E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD123D3E-8A09-4104-AF20-D5FBFAA6F52E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
88 changes: 88 additions & 0 deletions OpenAlprWebhookProcessor/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@

[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_code_quality_unused_parameters = all:suggestion

[*.cs]
csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
5 changes: 2 additions & 3 deletions OpenAlprWebhookProcessor/Alerts/Alert.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace OpenAlprWebhookProcessor.Alerts
{
public class Alert
{
public Guid Id { get; set; }

public bool IsUrgent { get; set; }

public string PlateNumber { get; set; }

public bool StrictMatch { get; set; }
Expand Down
51 changes: 8 additions & 43 deletions OpenAlprWebhookProcessor/Alerts/AlertService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
Expand All @@ -19,22 +18,18 @@ public class AlertService : IHostedService

private readonly CancellationTokenSource _cancellationTokenSource;

private readonly IServiceProvider _serviceProvider;

private readonly ILogger _logger;

private readonly IHubContext<ProcessorHub.ProcessorHub, ProcessorHub.IProcessorHub> _processorHub;

private readonly IEnumerable<IAlertClient> _alertClients;

public AlertService(
IServiceProvider serviceProvider,
ILogger<AlertService> logger,
IHubContext<ProcessorHub.ProcessorHub, ProcessorHub.IProcessorHub> processorHub,
IEnumerable<IAlertClient> alertClients)
{
_logger = logger;
_serviceProvider = serviceProvider;
_cancellationTokenSource = new CancellationTokenSource();
_alertsToProcess = new BlockingCollection<AlertUpdateRequest>();
_processorHub = processorHub;
Expand Down Expand Up @@ -67,48 +62,18 @@ private async Task ProcessAlertsAsync()
{
foreach (var job in _alertsToProcess.GetConsumingEnumerable(_cancellationTokenSource.Token))
{
using (var scope = _serviceProvider.CreateScope())
{
var processorContext = scope.ServiceProvider.GetRequiredService<ProcessorContext>();

var plateGroups = processorContext.PlateGroups.AsQueryable();
_logger.LogInformation("alerting for: {plateNumber}", job.PlateNumber);
await _processorHub.Clients.All.LicensePlateAlerted(job.PlateNumber);

if (job.IsStrictMatch)
{
plateGroups = plateGroups.Where(x => x.Id == job.LicensePlateId || x.PossibleNumbers.Any(x => x.Number == job.LicensePlateId.ToString()));
}
else
foreach (var alertClient in _alertClients)
{
try
{
plateGroups = plateGroups.Where(x => x.Id == job.LicensePlateId);
await alertClient.SendAlertAsync(job, _cancellationTokenSource.Token);
}

var result = await plateGroups
.Include(x => x.PlateImage)
.FirstOrDefaultAsync(_cancellationTokenSource.Token);

if (result != null)
catch (Exception ex)
{
_logger.LogInformation("alerting for: {alertId}", result.Id);
await _processorHub.Clients.All.LicensePlateAlerted(result.Id.ToString());

foreach (var alertClient in _alertClients)
{
try
{
await alertClient.SendAlertAsync(new Alert()
{
Description = job.Description,
Id = result.Id,
PlateNumber = result.BestNumber,
},
result.PlateImage.Jpeg,
_cancellationTokenSource.Token);
}
catch (Exception ex)
{
_logger.LogError(ex, $"failed to send alert to {nameof(alertClient)}");
}
}
_logger.LogError(ex, $"failed to send alert to {nameof(alertClient)}");
}
}
}
Expand Down
14 changes: 10 additions & 4 deletions OpenAlprWebhookProcessor/Alerts/AlertUpdateRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ namespace OpenAlprWebhookProcessor.Alerts
{
public class AlertUpdateRequest
{
public Guid CameraId { get; set; }

public string Description { get; set; }

public Guid LicensePlateId { get; set; }
public bool IsUrgent { get; set; }

public Guid PlateId { get; set; }

public string PlateNumber { get; set; }

public byte[] PlateJpeg { get; set; }

public string PlateJpegUrl { get; set; }

public bool IsStrictMatch { get; set; }
public DateTimeOffset ReceivedOn { get; set; }
}
}
5 changes: 3 additions & 2 deletions OpenAlprWebhookProcessor/Alerts/IAlertClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ namespace OpenAlprWebhookProcessor.Alerts
{
public interface IAlertClient
{
Task<bool> ShouldSendAllPlatesAsync(CancellationToken cancellationToken);

Task SendAlertAsync(
Alert alert,
byte[] plateJpeg,
AlertUpdateRequest alert,
CancellationToken cancellationToken);

Task VerifyCredentialsAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public async Task<PushoverRequest> HandleAsync(CancellationToken cancellationTok
ApiToken = client.ApiToken,
IsEnabled = client.IsEnabled,
SendPlatePreviewEnabled = client.SendPlatePreview,
SendEveryPlateEnabled = client.SendEveryPlateEnabled,
UserKey = client.UserKey,
};
}
Expand Down
Loading

0 comments on commit c169956

Please sign in to comment.