diff options
Diffstat (limited to 'BartService.cs')
| -rw-r--r-- | BartService.cs | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/BartService.cs b/BartService.cs new file mode 100644 index 0000000..f54ec55 --- /dev/null +++ b/BartService.cs @@ -0,0 +1,284 @@ +using Microsoft.EntityFrameworkCore; +using PagerParser.Bart; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace PagerParser { + public partial class PagerContext { + public DbSet<BartMember> BartMembers { get; set; } + public DbSet<BartAvailabilityRecord> BartAvailabilityRecords { get; set; } + } +} + +namespace PagerParser.Bart { + public enum BartStatus { + NotAvailable = -1, + Clear = 0, + Available = 1, + AtPremises = 2, + EmergencyOnly = 3, + Delayed = 4 + } + + [Index(nameof(MemberName))] + public record BartMember { + [JsonIgnore] + [Key] + public int BartMemberId { get; set; } + public string MemberName { get; set; } + } + + [Index(nameof(Hashcode))] + public record BartAvailabilityRecord { + [JsonIgnore] + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int BartAvailabilityRecordId { get; set; } + + public DateTime Timestamp { get; set; } + public BartMember Member { get; set; } + public BartStatus Status { get; set; } + public bool IsDefault { get; set; } + public DateTime? ModifiedOn { get; set; } + public BartMember? ModifiedBy { get; set; } + public DateTime? CreatedOn { get; set; } + public BartMember? CreatedBy { get; set; } + + public int Hashcode { + get => ( + Timestamp, + Member.BartMemberId, + Status, + IsDefault, + ModifiedOn, + ModifiedBy?.BartMemberId, + CreatedOn, + CreatedBy?.BartMemberId).GetHashCode(); + set => _ = value; + } + } + + public class BartService : IHostedService { + private const string AvailabilityEndpointUrl = + @"https://bartapp.net/webapp/webservice/WebsiteService.svc/GetUserAvailabilityList"; + + private ILogger logger; + private IServiceProvider serviceProvider; + + private HttpClient httpClient; + + public BartService( + ILogger<BartService> logger, + IServiceProvider serviceProvider) { + + this.logger = logger; + this.serviceProvider = serviceProvider; + } + + public async Task FetchAsync(DateTime fetchUntil) { + using var scope = serviceProvider.CreateScope(); + using var db = scope.ServiceProvider.GetRequiredService<PagerContext>(); + + // Prepare API request + var request = new BartAvailabilityRequest() { + RelatedDay = "2024-10-22", + // TODO: retrieve this from config + LoginToken = "<login token here>", + PermissionLevel = 2, + UserQualFilterIds = [], + AvailabilityGroupFilterId = "1366", + GroupId = "1366", + Length = 0 + }; + + // Fetch the data + var response = await httpClient.PostAsJsonAsync( + AvailabilityEndpointUrl, + request, + new JsonSerializerOptions() { + PropertyNamingPolicy = null + }); + + var responseData = await response.Content + .ReadFromJsonAsync<BartAvailabilityResponse>(); + + // Parse out a list of users from the response + var users = responseData.GetUserAvailabilityListResult.List + .Select(e => new BartMember() { + BartMemberId = e.UserId, + MemberName = e.MemberName + }) + .ToArray(); + + var existingUsers = db.BartMembers + .Select(m => m.BartMemberId) + .ToArray(); + + // Add new users to the DB and update the names of existing ones + foreach(var user in users) { + if(existingUsers.Contains(user.BartMemberId)) + db.Update(user); + else + db.Add(user); + } + + await db.SaveChangesAsync(); + + LinkedList<BartAvailabilityRecord> availabilityRecords = new(); + + foreach(var entry in responseData.GetUserAvailabilityListResult.List) { + for(int hour = 0; hour < 24; hour++) { + var timestamp = new DateTime( + DateOnly.FromDateTime(entry.RelatedDay), + new TimeOnly(hour, 0), + DateTimeKind.Local); + + var status = (BartStatus) entry.GetType() + .GetProperty($"Block{hour + 1}")! + .GetValue(entry)!; + + var record = new BartAvailabilityRecord() { + Timestamp = timestamp.ToUniversalTime(), + Member = users.First(u => u.BartMemberId == entry.UserId), + Status = status, + IsDefault = entry.AvailabilityId < 0, + ModifiedOn = entry.ModifiedOn, + ModifiedBy = users.FirstOrDefault(u => u.MemberName == entry.ModifiedBy), + CreatedBy = users.FirstOrDefault(u => u.MemberName == entry.CreatedBy) + }; + + // Optionally set the create/modify time if one is present in the fetched record + if(entry.ModifiedOn is not null) + record.ModifiedOn = + DateTime.SpecifyKind((DateTime) entry.ModifiedOn, DateTimeKind.Utc); + if(entry.CreatedOn is not null) + record.CreatedOn = + DateTime.SpecifyKind((DateTime) entry.CreatedOn, DateTimeKind.Utc); + + availabilityRecords.AddLast(record); + } + } + + var toAdd = availabilityRecords + .ExceptBy(db.BartAvailabilityRecords.Select(e => e.Hashcode), e => e.Hashcode); + + await db.BartAvailabilityRecords.AddRangeAsync(toAdd); + await db.SaveChangesAsync(); + } + + public async Task StartAsync(CancellationToken cancellationToken) { + logger.LogInformation("BART service starting..."); + httpClient = new(); + await FetchAsync(DateTime.Now); + } + + public Task StopAsync(CancellationToken cancellationToken) { + logger.LogInformation("BART service stopping..."); + httpClient.Dispose(); + return Task.CompletedTask; + } + + private record BartAvailabilityRequest { + public string RelatedDay { get; set; } + public string LoginToken { get; set; } + public int PermissionLevel { get; set; } + public string[] UserQualFilterIds { get; set; } + public string AvailabilityGroupFilterId { get; set; } + public string GroupId { get; set; } + [JsonPropertyName("length")] + public int Length { get; set; } + } + + private record BartAvailabilityResponse { + public BartAvailabilityResponseResult GetUserAvailabilityListResult { get; set; } + } + + private record BartAvailabilityResponseResult { + public List<BartAvailabilityResponseEntry> List { get; set; } + } + + private record BartAvailabilityResponseEntry { + public int AvailabilityId { get; set; } + [JsonConverter(typeof(BartDateTimeConverter))] + public DateTime RelatedDay { get; set; } + public string MemberName { get; set; } + [JsonConverter(typeof(BartNullableDateTimeConverter))] + public DateTime? CreatedOn { get; set; } + public string? CreatedBy { get; set; } + [JsonConverter(typeof(BartNullableDateTimeConverter))] + public DateTime? ModifiedOn { get; set; } + public string? ModifiedBy { get; set; } + public int UserId { get; set; } + public int Block1 { get; set; } + public int Block2 { get; set; } + public int Block3 { get; set; } + public int Block4 { get; set; } + public int Block5 { get; set; } + public int Block6 { get; set; } + public int Block7 { get; set; } + public int Block8 { get; set; } + public int Block9 { get; set; } + public int Block10 { get; set; } + public int Block11 { get; set; } + public int Block12 { get; set; } + public int Block13 { get; set; } + public int Block14 { get; set; } + public int Block15 { get; set; } + public int Block16 { get; set; } + public int Block17 { get; set; } + public int Block18 { get; set; } + public int Block19 { get; set; } + public int Block20 { get; set; } + public int Block21 { get; set; } + public int Block22 { get; set; } + public int Block23 { get; set; } + public int Block24 { get; set; } + } + + private class BartDateTimeConverter : JsonConverter<DateTime> { + private const string Format = "yyyy-MM-dd HH:mm:ss"; + + public override DateTime Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) { + + return DateTime.ParseExact(reader.GetString()!, Format, null); + } + + public override void Write( + Utf8JsonWriter writer, + DateTime value, + JsonSerializerOptions options) { + + writer.WriteStringValue(value.ToString(Format)); + } + } + + private class BartNullableDateTimeConverter : JsonConverter<DateTime?> { + private const string Format = "yyyy-MM-dd HH:mm:ss"; + + public override DateTime? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) { + + string? value = reader.GetString(); + if(string.IsNullOrEmpty(value)) + return null; + + return DateTime.ParseExact(value, Format, null); + } + + public override void Write( + Utf8JsonWriter writer, + DateTime? value, + JsonSerializerOptions options) { + + writer.WriteStringValue(value?.ToString(Format)); + } + } + } +} |
