Compare commits

..

2 Commits

Author SHA1 Message Date
a970012209
adding dialog to calendar
Some checks failed
ci / build-Docker (push) Has been cancelled
ci / build (8.0.x) (push) Has been cancelled
2024-06-10 22:46:17 +01:00
1d36c8d165
retrying network ops, filtering calendar by genuine only by default 2024-06-10 22:06:16 +01:00
8 changed files with 180 additions and 89 deletions

View File

@ -7,7 +7,7 @@ using Overflow.SouthernWater;
var driver = new MongoClient("mongodb://localhost"); var driver = new MongoClient("mongodb://localhost");
var api = new SouthernWaterApi(new HttpClient()); var api = new SouthernWaterApi(new HttpClient(), NullLogger<SouthernWaterApi>.Instance);
await api.LoadApiUrl(); await api.LoadApiUrl();
var runner = new SouthernWaterApiJobRunnerPersisting(api, NullLogger<SouthernWaterApiJobRunner>.Instance, driver.GetDatabase(Static.DatabaseName)); var runner = new SouthernWaterApiJobRunnerPersisting(api, NullLogger<SouthernWaterApiJobRunner>.Instance, driver.GetDatabase(Static.DatabaseName));

View File

@ -1,3 +1,6 @@
using Microsoft.Extensions.Logging.Abstractions;
using Overflow.SouthernWater;
namespace Overflow.Test; namespace Overflow.Test;
public class Tests public class Tests
@ -10,7 +13,7 @@ public class Tests
[Test] [Test]
public async Task Test1() public async Task Test1()
{ {
var southern = new SouthernWater.SouthernWaterApi(new HttpClient()); var southern = new SouthernWater.SouthernWaterApi(new HttpClient(), NullLogger<SouthernWaterApi>.Instance);
await southern.LoadApiUrl(); await southern.LoadApiUrl();
var spills = await southern.GetSpills(); var spills = await southern.GetSpills();
} }

View File

@ -0,0 +1,47 @@
@using Overflow.SouthernWater
@rendermode RenderMode.InteractiveAuto
<RadzenStack>
<RadzenStack Gap="0">
<RadzenText TextStyle="TextStyle.Overline" class="rz-display-flex rz-mt-2 rz-my-0">Bathing Site</RadzenText>
<RadzenText TextStyle="TextStyle.Body1"><b>@Spill.bathingSite</b></RadzenText>
<RadzenText TextStyle="TextStyle.Overline" class="rz-display-flex rz-mt-4 rz-mb-0">Outfall</RadzenText>
<RadzenText TextStyle="TextStyle.Body1"><b>@Spill.outfallName</b></RadzenText>
</RadzenStack>
<RadzenStack Gap="0">
<RadzenText TextStyle="TextStyle.Overline">Status: </RadzenText>
<RadzenStack Orientation="Orientation.Horizontal">
<RadzenText TextStyle="TextStyle.Body1"><b>@Spill.status</b></RadzenText>
@if (Spill.status == "Genuine")
{
<RadzenIcon Icon="verified" />
}
</RadzenStack>
<RadzenText TextStyle="TextStyle.Overline">Is Impacting</RadzenText>
<RadzenStack Orientation="Orientation.Horizontal">
@if (Spill.isImpacting)
{
<RadzenIcon Icon="check_circle" />
}
else
{
<RadzenIcon Icon="cancel" />
}
</RadzenStack>
</RadzenStack>
<RadzenStack Gap="0">
<RadzenText TextStyle="TextStyle.Overline" class="rz-display-flex rz-mt-2 rz-my-0">Start</RadzenText>
<RadzenText TextStyle="TextStyle.Body1"><b>@Spill.eventStart.ToLongDateString()</b> @Spill.eventStart.ToLongTimeString()</RadzenText>
<RadzenText TextStyle="TextStyle.Overline" class="rz-display-flex rz-mt-4 rz-mb-0">End</RadzenText>
<RadzenText TextStyle="TextStyle.Body1"><b>@Spill.eventStop.ToLongDateString()</b> @Spill.eventStop.ToLongTimeString()</RadzenText>
<RadzenStack Orientation="Orientation.Vertical">
<RadzenText TextStyle="TextStyle.Overline" class="rz-display-flex rz-mt-4 rz-mb-0">Duration</RadzenText>
<RadzenText TextStyle="TextStyle.Body1">@TimeSpan.FromMinutes(Spill.duration).ToString()</RadzenText>
</RadzenStack>
</RadzenStack>
</RadzenStack>
@code {
[Parameter] public Spill Spill { get; set; }
}

View File

@ -10,7 +10,7 @@
} }
else else
{ {
<RadzenScheduler @ref=@scheduler SlotRender=@OnSlotRender style="height: 768px;" TItem="Spill" Data=@Job.Spills StartProperty="eventStart" EndProperty="eventStop" <RadzenScheduler @ref=@scheduler SlotRender=@OnSlotRender style="height: 768px;" TItem="Spill" Data=@Spills StartProperty="eventStart" EndProperty="eventStop"
TextProperty="bathingSite" SelectedIndex="2" TextProperty="bathingSite" SelectedIndex="2"
SlotSelect=@OnSlotSelect AppointmentSelect=@OnAppointmentSelect AppointmentRender=@OnAppointmentRender SlotSelect=@OnSlotSelect AppointmentSelect=@OnAppointmentSelect AppointmentRender=@OnAppointmentRender
AppointmentMove=@OnAppointmentMove > AppointmentMove=@OnAppointmentMove >
@ -26,9 +26,21 @@ else
@code { @code {
RadzenScheduler<Spill> scheduler; RadzenScheduler<Spill> scheduler;
[Parameter] public SouthernWaterApiJob? Job { get; set; } [Parameter] public SouthernWaterApiJob? Job { get; set; }
[Parameter] public bool GenuineOnly { get; set; } = true;
Dictionary<DateTime, string> events = new Dictionary<DateTime, string>(); private IEnumerable<Spill> Spills {
get
{
if (GenuineOnly)
{
return Job.Spills.Where(j => j.status == "Genuine");
}
else
{
return Job.Spills;
}
}
}
void OnSlotRender(SchedulerSlotRenderEventArgs args) void OnSlotRender(SchedulerSlotRenderEventArgs args)
{ {
@ -63,24 +75,9 @@ else
async Task OnAppointmentSelect(SchedulerAppointmentSelectEventArgs<Spill> args) async Task OnAppointmentSelect(SchedulerAppointmentSelectEventArgs<Spill> args)
{ {
// var copy = new Appointment _ = await DialogService.OpenAsync<SpillViewDialog>("View Spill", new Dictionary<string, object> { { "Spill", args.Data } });
// {
// Start = args.Data.Start, await scheduler.Reload();
// End = args.Data.End,
// Text = args.Data.Text
// };
//
// var data = await DialogService.OpenAsync<EditAppointmentPage>("Edit Appointment", new Dictionary<string, object> { { "Appointment", copy } });
//
// if (data != null)
// {
// // Update the appointment
// args.Data.Start = data.Start;
// args.Data.End = data.End;
// args.Data.Text = data.Text;
// }
//
// await scheduler.Reload();
} }
void OnAppointmentRender(SchedulerAppointmentRenderEventArgs<Spill> args) void OnAppointmentRender(SchedulerAppointmentRenderEventArgs<Spill> args)

View File

@ -16,4 +16,4 @@
<a class="dismiss">🗙</a> <a class="dismiss">🗙</a>
</div> </div>
<RadzenComponents/> <RadzenComponents @rendermode="InteractiveServer"/>

View File

@ -15,15 +15,20 @@
{ {
<RadzenText TextStyle="TextStyle.Body1">Last updated at <b>@job.EndTime</b></RadzenText> <RadzenText TextStyle="TextStyle.Body1">Last updated at <b>@job.EndTime</b></RadzenText>
} }
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Start" Wrap="FlexWrap.Wrap">
<RadzenCheckBox @bind-Value=@genuineOnly Name="genuineOnly" />
<RadzenLabel Text="Genuine Events Only" Component="genuineOnly" Style="margin-left: 8px; vertical-align: middle;" />
</RadzenStack>
</RadzenStack> </RadzenStack>
</RadzenCard> </RadzenCard>
<SpillsCalendar Job="@job" /> <SpillsCalendar Job="@job" GenuineOnly="@genuineOnly" />
@code { @code {
private SouthernWaterApiJob? job; private SouthernWaterApiJob? job;
[Inject] private IMongoDatabase database { get; set; } [Inject] private IMongoDatabase database { get; set; }
// private bool showIds; // private bool showIds;
private bool genuineOnly = true;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {

View File

@ -1,6 +1,7 @@
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using Quartz.Util; using Quartz.Util;
namespace Overflow.SouthernWater; namespace Overflow.SouthernWater;
@ -8,21 +9,29 @@ namespace Overflow.SouthernWater;
public partial class SouthernWaterApi public partial class SouthernWaterApi
{ {
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ILogger<SouthernWaterApi> _logger;
private static readonly string spillsEndpoint = "Spills/GetHistoricSpills"; private static readonly string spillsEndpoint = "Spills/GetHistoricSpills";
private string baseUrl; private string baseUrl;
private string apiKey; private string apiKey;
public SouthernWaterApi(HttpClient client) public SouthernWaterApi(HttpClient client, ILogger<SouthernWaterApi> logger)
{ {
_client = client; _client = client;
_logger = logger;
} }
[GeneratedRegex(@".*const APIURL = '(?<APIURL>.*)'.*\n.*const APIGWKEY = '(?<APIKEY>.*)'.*", RegexOptions.IgnoreCase)] [GeneratedRegex(@".*const APIURL = '(?<APIURL>.*)'.*\n.*const APIGWKEY = '(?<APIKEY>.*)'.*", RegexOptions.IgnoreCase)]
private static partial Regex ApiUrlAndKey(); private static partial Regex ApiUrlAndKey();
public async Task LoadApiUrl() public async Task LoadApiUrl()
{
var success = false;
while (!success)
{
try
{ {
var request = new HttpRequestMessage var request = new HttpRequestMessage
{ {
@ -45,6 +54,9 @@ public partial class SouthernWaterApi
var content = await _client.SendAsync(request); var content = await _client.SendAsync(request);
content.EnsureSuccessStatusCode(); content.EnsureSuccessStatusCode();
success = true;
var contentString = await content.Content.ReadAsStringAsync(); var contentString = await content.Content.ReadAsStringAsync();
Match m = ApiUrlAndKey().Match(contentString); Match m = ApiUrlAndKey().Match(contentString);
@ -62,28 +74,45 @@ public partial class SouthernWaterApi
this.apiKey = apiKey.Value; this.apiKey = apiKey.Value;
} }
} }
catch (HttpRequestException e)
{
_logger.LogError(e, "HTTP Exception while API details, waiting {} before retrying", Static.Interval);
await Task.Delay(Static.Interval);
}
}
}
public async Task<PagedItems<Spill>?> GetSpills(int page = 1, JsonSerialiser? jsonSerialiser = null) public async Task<PagedItems<Spill>?> GetSpills(int page = 1, JsonSerialiser? jsonSerialiser = null)
{ {
if (baseUrl.IsNullOrWhiteSpace()) await LoadApiUrl(); if (baseUrl.IsNullOrWhiteSpace()) await LoadApiUrl();
PagedItems<Spill>? parsedPage = null;
var success = false;
while (!success)
{
try
{
var request = new HttpRequestMessage() var request = new HttpRequestMessage()
{ {
RequestUri = new Uri(baseUrl + spillsEndpoint + "?page=" + page), RequestUri = new Uri(baseUrl + spillsEndpoint + "?page=" + page),
Method = HttpMethod.Get, Method = HttpMethod.Get,
Headers = Headers =
{ {
{"Accept", "*/*"}, { "Accept", "*/*" },
{"Accept-Language", "en-GB,en;q=0.5"}, { "Accept-Language", "en-GB,en;q=0.5" },
// {"Accept-Encoding", "gzip, deflate, br, zstd"}, // {"Accept-Encoding", "gzip, deflate, br, zstd"},
{"Cache-Control", "no-cache"}, { "Cache-Control", "no-cache" },
{"Connection", "keep-alive"}, { "Connection", "keep-alive" },
{"DNT", "1"}, { "DNT", "1" },
{"User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:126.0) Gecko/20100101 Firefox/126.0"}, { "User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:126.0) Gecko/20100101 Firefox/126.0" },
{"Upgrade-Insecure-Requests", "1"}, { "Upgrade-Insecure-Requests", "1" },
{"Referer", "https://www.southernwater.co.uk/our-region/clean-rivers-and-seas-task-force/beachbuoy-historic-release-table/"}, {
{"x-Gateway-APIKey", apiKey}, "Referer",
{"X-Requested-With", "XMLHttpRequest"}, "https://www.southernwater.co.uk/our-region/clean-rivers-and-seas-task-force/beachbuoy-historic-release-table/"
},
{ "x-Gateway-APIKey", apiKey },
{ "X-Requested-With", "XMLHttpRequest" },
} }
}; };
@ -91,7 +120,10 @@ public partial class SouthernWaterApi
content.EnsureSuccessStatusCode(); content.EnsureSuccessStatusCode();
var parsedPage = (PagedItems<Spill>?) await content.Content.ReadFromJsonAsync(typeof(PagedItems<Spill>), jsonSerialiser ?? new JsonSerialiser()); success = true;
parsedPage = (PagedItems<Spill>?)await content.Content.ReadFromJsonAsync(typeof(PagedItems<Spill>),
jsonSerialiser ?? new JsonSerialiser());
if (parsedPage is not null) if (parsedPage is not null)
{ {
@ -101,6 +133,13 @@ public partial class SouthernWaterApi
x.eventStop = x.eventStop.ToUniversalTime(); x.eventStop = x.eventStop.ToUniversalTime();
}); });
} }
}
catch (HttpRequestException e)
{
_logger.LogError(e, "HTTP Exception while loading page [{}], waiting {} before retrying", page, Static.Interval);
await Task.Delay(Static.Interval);
}
}
return parsedPage; return parsedPage;
} }