diff --git a/CHANGELOG.md b/CHANGELOG.md index fb21317..7864089 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,3 +12,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implemented species extraction to JSON and CSV. - Implemented a Web API. - Implemented roster management. +- Implemented roster statistics. diff --git a/src/PokeData.Application/Roster/Queries/ReadPokemonRosterQueryHandler.cs b/src/PokeData.Application/Roster/Queries/ReadPokemonRosterQueryHandler.cs index b1e8452..05ef0ea 100644 --- a/src/PokeData.Application/Roster/Queries/ReadPokemonRosterQueryHandler.cs +++ b/src/PokeData.Application/Roster/Queries/ReadPokemonRosterQueryHandler.cs @@ -1,19 +1,73 @@ using MediatR; +using PokeData.Application.Regions; +using PokeData.Application.Types; +using PokeData.Contracts.Regions; using PokeData.Contracts.Roster; +using PokeData.Contracts.Search; +using PokeData.Contracts.Types; namespace PokeData.Application.Roster.Queries; internal class ReadPokemonRosterQueryHandler : IRequestHandler { + private readonly IRegionQuerier _regionQuerier; private readonly IPokemonRosterQuerier _rosterQuerier; + private readonly IPokemonTypeQuerier _typeQuerier; - public ReadPokemonRosterQueryHandler(IPokemonRosterQuerier rosterQuerier) + public ReadPokemonRosterQueryHandler(IRegionQuerier regionQuerier, IPokemonRosterQuerier rosterQuerier, IPokemonTypeQuerier typeQuerier) { + _regionQuerier = regionQuerier; _rosterQuerier = rosterQuerier; + _typeQuerier = typeQuerier; } public async Task Handle(ReadPokemonRosterQuery _, CancellationToken cancellationToken) { - return await _rosterQuerier.ReadAsync(cancellationToken); + PokemonRoster roster = await _rosterQuerier.ReadAsync(cancellationToken); + + SearchResults regions = await _regionQuerier.SearchAsync(new SearchRegionsPayload(), cancellationToken); + Dictionary regionCount = new(capacity: regions.Items.Count); + foreach (Region region in regions.Items) + { + string key = region.DisplayName ?? region.UniqueName; + regionCount[key] = 0; + } + + SearchResults types = await _typeQuerier.SearchAsync(new SearchPokemonTypesPayload(), cancellationToken); + Dictionary typeCount = new(capacity: types.Items.Count); + foreach (PokemonType type in types.Items) + { + string key = type.DisplayName ?? type.UniqueName; + typeCount[key] = 0; + } + + ushort selected = 0; + ushort selectedTypes = 0; + foreach (RosterItem item in roster.Items) + { + if (item.Destination != null) + { + selected++; + selectedTypes++; + + regionCount[item.Destination.Region]++; + typeCount[item.Destination.PrimaryType]++; + + if (item.Destination.SecondaryType != null) + { + selectedTypes++; + typeCount[item.Destination.SecondaryType]++; + } + } + } + + roster.Stats = new RosterStatistics + { + Count = selected, + Regions = regionCount.Select(pair => RosterStatistic.Create(pair.Key, pair.Value, selected)).ToList(), + Types = typeCount.Select(pair => RosterStatistic.Create(pair.Key, pair.Value, selectedTypes)).ToList() + }; + + return roster; } } diff --git a/src/PokeData.Contracts/Roster/PokemonRoster.cs b/src/PokeData.Contracts/Roster/PokemonRoster.cs index 7656724..f07d7ff 100644 --- a/src/PokeData.Contracts/Roster/PokemonRoster.cs +++ b/src/PokeData.Contracts/Roster/PokemonRoster.cs @@ -3,5 +3,5 @@ public record PokemonRoster { public List Items { get; set; } = []; - // TODO(fpion): Stats + public RosterStatistics Stats { get; set; } = new(); } diff --git a/src/PokeData.Contracts/Roster/RosterStatistic.cs b/src/PokeData.Contracts/Roster/RosterStatistic.cs new file mode 100644 index 0000000..89effb1 --- /dev/null +++ b/src/PokeData.Contracts/Roster/RosterStatistic.cs @@ -0,0 +1,15 @@ +namespace PokeData.Contracts.Roster; + +public record RosterStatistic +{ + public string Key { get; set; } = string.Empty; + public int Count { get; set; } + public double Percentage { get; set; } + + public static RosterStatistic Create(string key, int count, int total) => new() + { + Key = key, + Count = count, + Percentage = count / (double)total + }; +} diff --git a/src/PokeData.Contracts/Roster/RosterStatistics.cs b/src/PokeData.Contracts/Roster/RosterStatistics.cs new file mode 100644 index 0000000..48e4f4e --- /dev/null +++ b/src/PokeData.Contracts/Roster/RosterStatistics.cs @@ -0,0 +1,8 @@ +namespace PokeData.Contracts.Roster; + +public record RosterStatistics +{ + public ushort Count { get; set; } + public List Regions { get; set; } = []; + public List Types { get; set; } = []; +}