diff --git a/Expedience.Api/Controllers/ExpedienceController.cs b/Expedience.Api/Controllers/ExpedienceController.cs index fe34243..259235c 100644 --- a/Expedience.Api/Controllers/ExpedienceController.cs +++ b/Expedience.Api/Controllers/ExpedienceController.cs @@ -79,7 +79,7 @@ namespace Expedience.Api.Controllers try { using var scope = _serviceScopeFactory.CreateScope(); - var repository = scope.ServiceProvider.GetService(); + var repository = scope.ServiceProvider.GetRequiredService(); var results = await repository.GetDutyCompletionRecords(expac, cancellationToken); return Ok(results); @@ -92,14 +92,13 @@ namespace Expedience.Api.Controllers return StatusCode(500); } - [HttpGet("DeepDungeonRecords")] public async Task GetDeepDungeonRecords(CancellationToken cancellationToken) { try { using var scope = _serviceScopeFactory.CreateScope(); - var repository = scope.ServiceProvider.GetService(); + var repository = scope.ServiceProvider.GetRequiredService(); var results = await repository.GetDeepDungeonRecords(cancellationToken); return Ok(results); @@ -112,13 +111,32 @@ namespace Expedience.Api.Controllers return StatusCode(500); } + [HttpGet("TreasureHuntRecords")] + public async Task GetTreasureHuntRecords(CancellationToken cancellationToken) + { + try + { + using var scope = _serviceScopeFactory.CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService(); + + var results = await repository.GetContentTypeRecords("Treasure Hunt", cancellationToken); + return Ok(results); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error getting Treasure Hunt Records: {errorMessage}", ex.Message); + } + + return StatusCode(500); + } + [HttpGet("TopX/{recordType}/{territoryId}/{limit}")] public async Task GetTopXRecords(int recordType, int territoryId, int limit, CancellationToken cancellationToken) { try { using var scope = _serviceScopeFactory.CreateScope(); - var repository = scope.ServiceProvider.GetService(); + var repository = scope.ServiceProvider.GetRequiredService(); var records = await repository.GetTopXRecords(recordType, territoryId, limit, cancellationToken); diff --git a/Expedience.Infrastructure/ExpedienceContext.cs b/Expedience.Infrastructure/ExpedienceContext.cs index 4941384..1bf6945 100644 --- a/Expedience.Infrastructure/ExpedienceContext.cs +++ b/Expedience.Infrastructure/ExpedienceContext.cs @@ -12,14 +12,12 @@ namespace Expedience.Infrastructure } public DbSet DutyCompletionResults { get; set; } - public DbSet DutyMembers { get; set; } - public DbSet Territories { get; set; } - public DbSet Users { get; set; } public DbSet DutyCompletionRecords { get; set; } public DbSet DeepDungeonRecords { get; set; } + public DbSet ContentTypeRecords { get; set; } public DbSet TopXRecords { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -78,6 +76,10 @@ namespace Expedience.Infrastructure .HasNoKey() .ToTable(t => t.ExcludeFromMigrations()); + modelBuilder.Entity() + .HasNoKey() + .ToTable(t => t.ExcludeFromMigrations()); + modelBuilder.Entity(cfg => { cfg.HasNoKey(); diff --git a/Expedience.Infrastructure/ExpedienceRepository.cs b/Expedience.Infrastructure/ExpedienceRepository.cs index dc2d6f1..616ee40 100644 --- a/Expedience.Infrastructure/ExpedienceRepository.cs +++ b/Expedience.Infrastructure/ExpedienceRepository.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using EFCore.NamingConventions.Internal; using Enyim.Caching; -using Expedience.Infrasctructure.Models; using Expedience.Infrastructure.Models; using Expedience.Models; using Microsoft.EntityFrameworkCore; @@ -13,6 +10,7 @@ public interface IExpedienceRepository { ValueTask> GetDutyCompletionRecords(string expac, CancellationToken cancellationToken); ValueTask> GetDeepDungeonRecords(CancellationToken cancellationToken); + ValueTask> GetContentTypeRecords(string contentType, CancellationToken cancellationToken); ValueTask> GetTopXRecords(int recordType, int territoryId, int limit, CancellationToken cancellationToken); } @@ -146,4 +144,15 @@ public class ExpedienceRepository : IExpedienceRepository return records; } + + public async ValueTask> GetContentTypeRecords(string contentType, CancellationToken cancellationToken) + { + var cacheKey = $"xpd-th"; + var cacheSeconds = 600; + var records = await _memcachedClient.GetValueOrCreateAsync(cacheKey, cacheSeconds, + async () => await _dbContext.ContentTypeRecords.FromSqlInterpolated($"SELECT * FROM public.get_contenttyperecords({contentType})") + .ToListAsync(cancellationToken)); + + return records; + } } diff --git a/Expedience.Infrastructure/Models/ContentTypeRecord.cs b/Expedience.Infrastructure/Models/ContentTypeRecord.cs new file mode 100644 index 0000000..85e08c0 --- /dev/null +++ b/Expedience.Infrastructure/Models/ContentTypeRecord.cs @@ -0,0 +1,15 @@ +using System; + +namespace Expedience.Infrastructure.Models; + +public class ContentTypeRecord +{ + public int TerritoryId { get; set; } + public string PlaceName { get; set; } = null!; + public string ContentName { get; set; } = null!; + public int Level { get; set; } + public TimeSpan? MinParty { get; set; } + public TimeSpan? AvgParty { get; set; } + public TimeSpan? MinSolo { get; set; } + public TimeSpan? AvgSolo { get; set; } +} diff --git a/Expedience.Models/UserInfo.cs b/Expedience.Models/UserInfo.cs index 27b6db0..b5446b3 100644 --- a/Expedience.Models/UserInfo.cs +++ b/Expedience.Models/UserInfo.cs @@ -11,5 +11,6 @@ namespace Expedience.Models public string UserId { get; set; } public int WorldId { get; set; } public string WorldName { get; set; } - } + public string UserName { get; set; } + } } diff --git a/Expedience.Web/Pages/Index.cshtml.cs b/Expedience.Web/Pages/Index.cshtml.cs index 64e3f02..bfbdfbd 100644 --- a/Expedience.Web/Pages/Index.cshtml.cs +++ b/Expedience.Web/Pages/Index.cshtml.cs @@ -11,8 +11,9 @@ namespace Expedience.Web.Pages public List? DutyCompletionRecords { get; private set; } public List? DeepDungeonRecords { get; private set; } + public List? TreasureHuntRecords { get; private set; } - public IndexModel(IApiService apiService) + public IndexModel(IApiService apiService) { _apiService = apiService; } @@ -24,10 +25,18 @@ namespace Expedience.Web.Pages if (expac == "DeepDungeons") { DutyCompletionRecords = null; + TreasureHuntRecords = null; DeepDungeonRecords = await _apiService.GetDeepDungeonRecordsAsync(cancellationToken); } + else if (expac == "TreasureHunt") + { + TreasureHuntRecords = await _apiService.GetTreasureHuntRecordsAsync(cancellationToken); + DutyCompletionRecords = null; + DeepDungeonRecords = null; + } else { + TreasureHuntRecords = null; DutyCompletionRecords = await _apiService.GetDutyCompletionRecordsAsync(expac, cancellationToken); DeepDungeonRecords = null; } diff --git a/Expedience.Web/Pages/Shared/_Layout.cshtml b/Expedience.Web/Pages/Shared/_Layout.cshtml index 691cc23..045ed5b 100644 --- a/Expedience.Web/Pages/Shared/_Layout.cshtml +++ b/Expedience.Web/Pages/Shared/_Layout.cshtml @@ -48,6 +48,9 @@ + diff --git a/Expedience.Web/Pages/_TreasureHuntTable .cshtml b/Expedience.Web/Pages/_TreasureHuntTable .cshtml new file mode 100644 index 0000000..ca02fa7 --- /dev/null +++ b/Expedience.Web/Pages/_TreasureHuntTable .cshtml @@ -0,0 +1,34 @@ +@using Expedience.Infrastructure.Models; +@using Expedience.Web.Extensions; +@{ +@model IEnumerable +} + + + + + + + + + + + + + + + + + + @foreach (var record in Model) + { + + + + + + + + } + +
PartySolo
Deep DungeonMinAvgMinAvg
@record.ContentName@record.MinParty.Format()@record.AvgParty.Format()@record.MinSolo.Format()@record.AvgSolo.Format()
\ No newline at end of file diff --git a/Expedience.Web/Services/ApiService.cs b/Expedience.Web/Services/ApiService.cs index 44de81b..0a76c91 100644 --- a/Expedience.Web/Services/ApiService.cs +++ b/Expedience.Web/Services/ApiService.cs @@ -8,13 +8,14 @@ public interface IApiService { public ValueTask?> GetDutyCompletionRecordsAsync(string expac, CancellationToken cancellationToken); public ValueTask?> GetDeepDungeonRecordsAsync(CancellationToken cancellationToken); + public ValueTask?> GetTreasureHuntRecordsAsync(CancellationToken cancellationToken); public ValueTask?> GetTopXRecords(int recordType, int territoryId, int limit, CancellationToken cancellationToken); } public class ApiService : IApiService { - private ILogger _logger; - private HttpClient _httpClient { get; } + private readonly ILogger _logger; + private readonly HttpClient _httpClient; public ApiService(ILogger logger, HttpClient httpClient) { @@ -37,7 +38,7 @@ public class ApiService : IApiService expac, ex.Message); } - return new List(); + return []; } public async ValueTask?> GetDeepDungeonRecordsAsync(CancellationToken cancellationToken) @@ -54,7 +55,7 @@ public class ApiService : IApiService _logger.LogError(ex, "An error occurred trying to retrieve Deep Dungeon Records: {errorMessage}", ex.Message); } - return new List(); + return []; } public async ValueTask> GetTopXRecords(int recordType, int territoryId, int limit, CancellationToken cancellationToken) @@ -74,6 +75,24 @@ public class ApiService : IApiService limit, territoryId, recordType, ex.Message); } - return new List(); + return []; + } + + public async ValueTask?> GetTreasureHuntRecordsAsync(CancellationToken cancellationToken) + { + var stopwatch = Stopwatch.StartNew(); + try + { + var records = await _httpClient.GetFromJsonAsync?>($"/api/TreasureHuntRecords", cancellationToken); + _logger.LogInformation("Retrieved Treasure Hunt Records in {duration}ms", stopwatch.ElapsedMilliseconds); + + return records; + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred trying to retrieve Treasure Hunt Records: {errorMessage}", ex.Message); + } + + return []; } }