Skip to content

Commit

Permalink
feat: implement loading and calculating the vary of country-statistics
Browse files Browse the repository at this point in the history
  • Loading branch information
alsami committed Feb 1, 2021
1 parent 196712f commit 96c9704
Show file tree
Hide file tree
Showing 18 changed files with 352 additions and 6 deletions.
15 changes: 15 additions & 0 deletions Covid19Api.sln
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Covid19Api.AutoMapper.Tests", "test\Covid19Api.AutoMapper.Tests\Covid19Api.AutoMapper.Tests.csproj", "{7D85801F-6AF5-407B-B262-2F85E6D8CF7D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Covid19Api.Services.Tests", "test\Covid19Api.Services.Tests\Covid19Api.Services.Tests.csproj", "{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -336,6 +338,18 @@ Global
{7D85801F-6AF5-407B-B262-2F85E6D8CF7D}.Release|x64.Build.0 = Release|Any CPU
{7D85801F-6AF5-407B-B262-2F85E6D8CF7D}.Release|x86.ActiveCfg = Release|Any CPU
{7D85801F-6AF5-407B-B262-2F85E6D8CF7D}.Release|x86.Build.0 = Release|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Debug|x64.ActiveCfg = Debug|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Debug|x64.Build.0 = Debug|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Debug|x86.ActiveCfg = Debug|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Debug|x86.Build.0 = Debug|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Release|Any CPU.Build.0 = Release|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Release|x64.ActiveCfg = Release|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Release|x64.Build.0 = Release|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Release|x86.ActiveCfg = Release|Any CPU
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1A670B81-08F0-4F15-81AF-4DDD12E765D6} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
Expand All @@ -359,5 +373,6 @@ Global
{8C8534AB-D819-4C9B-BF63-C750646AF787} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
{F135C222-3190-4379-A0B9-35A519E535A6} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
{7D85801F-6AF5-407B-B262-2F85E6D8CF7D} = {EBCCA0C8-4218-490F-A131-E8DF265DC415}
{0D3D25E7-6045-41C1-B63A-AFCCFE93C403} = {EBCCA0C8-4218-490F-A131-E8DF265DC415}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public override async Task<CountryStatisticsGrpcMessage> LoadHistoricalCountrySt
CountryStatisticsForCountryGrpcMessage request,
ServerCallContext context)
{
var query = new LoadHistoricalCountryStatisticsForCountryQuery(request.Country);
var query = new LoadHistoricalCountryStatisticsForCountryQuery(request.Country, DateTime.UtcNow.Date.AddDays(-9));
var countryStatistics = await this.mediator.Send(query);
return new CountryStatisticsGrpcMessage
{
Expand Down
11 changes: 10 additions & 1 deletion src/Covid19Api.Endpoints.Rest/V1/CountryStatisticsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,22 @@ public Task<IEnumerable<CountryStatisticDto>> LoadLatestAsync() =>
public Task<IEnumerable<CountryStatisticDto>> LoadHistoryAsync()
=> this.mediator.Send(new LoadHistoricalCountriesStatisticsQuery(DateTime.UtcNow.Date.AddDays(-9)));

[HttpGet("history/vary")]
public Task<IEnumerable<CountryVaryStatisticContainerDto>> LoadVaryHistoryAsync()
=> this.mediator.Send(new CalculateVaryForCountriesStatisticsQuery(DateTime.UtcNow.Date.AddDays(-9)));

[HttpGet("{country}")]
public Task<CountryStatisticDto> LoadLatestForCountryAsync(string country) =>
this.mediator.Send(new LoadLatestStatisticsForCountryQuery(country));

[HttpGet("{country}/history")]
public Task<IEnumerable<CountryStatisticDto>> LoadHistoryForCountryAsync(string country) =>
this.mediator.Send(new LoadHistoricalCountryStatisticsForCountryQuery(country));
this.mediator.Send(
new LoadHistoricalCountryStatisticsForCountryQuery(country, DateTime.UtcNow.Date.AddDays(-9)));

[HttpGet("{country}/history/vary")]
public Task<IEnumerable<CountryVaryStatisticContainerDto>> LoadVaryHistoryForCountryAsync(string country) =>
this.mediator.Send(new CalculateVaryForCountryStatisticsQuery(country, DateTime.UtcNow.Date.AddDays(-9)));

[HttpGet("{countryCode}/flag")]
public async Task<IActionResult> LoadFlagAsync(string countryCode)
Expand Down
6 changes: 6 additions & 0 deletions src/Covid19Api.IoC/Extensions/ContainerBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
using Covid19Api.IoC.Modules;
using Covid19Api.Repositories;
using Covid19Api.Repositories.Abstractions;
using Covid19Api.Services.Abstractions.Calculators;
using Covid19Api.Services.Abstractions.Compression;
using Covid19Api.Services.Abstractions.Loader;
using Covid19Api.Services.Calculators;
using Covid19Api.Services.Compression;
using Covid19Api.Services.Decorator;
using Covid19Api.Services.Loader;
Expand Down Expand Up @@ -60,6 +62,10 @@ public static ContainerBuilder RegisterRepositories(this ContainerBuilder builde

public static ContainerBuilder RegisterServices(this ContainerBuilder builder)
{
builder.RegisterType<CountryVaryStatisticsCalculator>()
.As<ICountryVaryStatisticsCalculator>()
.SingleInstance();

builder.RegisterType<BrotliCompressionService>()
.As<ICompressionService>()
.SingleInstance();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System;
using System.Collections.Generic;

namespace Covid19Api.Presentation.Response
{
public record CountryVaryStatisticContainerDto(DateTime Time, IEnumerable<CountryVaryStatisticDto> Vary);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Covid19Api.Presentation.Response
{
public record CountryVaryStatisticDto(string ValueType, double? Vary, int? ValueYesterday, int ValueToday);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using Covid19Api.Domain;
using Covid19Api.Presentation.Response;

namespace Covid19Api.Services.Abstractions.Calculators
{
public interface ICountryVaryStatisticsCalculator
{
IEnumerable<CountryVaryStatisticContainerDto> Calculate(IEnumerable<CountryStatistic> countryStatistics);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<ItemGroup>
<ProjectReference Include="..\Covid19Api.Domain\Covid19Api.Domain.csproj" />
<ProjectReference Include="..\Covid19Api.Presentation\Covid19Api.Presentation.csproj" />
</ItemGroup>

</Project>
104 changes: 104 additions & 0 deletions src/Covid19Api.Services/Calculators/CountryVaryStatisticsCalculator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Covid19Api.Domain;
using Covid19Api.Presentation.Response;
using Covid19Api.Services.Abstractions.Calculators;

namespace Covid19Api.Services.Calculators
{
public class CountryVaryStatisticsCalculator : ICountryVaryStatisticsCalculator
{
public IEnumerable<CountryVaryStatisticContainerDto> Calculate(IEnumerable<CountryStatistic> countryStatistics)
{
var countryStatisticsByFetchedAt = countryStatistics
.OrderBy(statistic => statistic.FetchedAt)
.GroupBy(statistic => statistic.FetchedAt)
.ToList();

for (var i = 0; i < countryStatisticsByFetchedAt.Count; i++)
{
var currentGroup = countryStatisticsByFetchedAt.ElementAt(i);

if (i == 0)
{
yield return CreateEmpty(currentGroup.Key.Date, currentGroup.ToList());
continue;
}

var previousGroup = countryStatisticsByFetchedAt.ElementAt(i - 1);

yield return new CountryVaryStatisticContainerDto(currentGroup.Key.Date, Calculate(currentGroup.ToList(), previousGroup.ToList()));
}
}

private static IEnumerable<CountryVaryStatisticDto> Calculate(IReadOnlyList<CountryStatistic> current,
IReadOnlyList<CountryStatistic> previous)
{
var currentTotalSum = current.Sum(c => c.TotalCases);
var previousTotalSum = previous.Sum(c => c.TotalCases);
yield return new CountryVaryStatisticDto(ValueKeys.Total,
CalculateVary(currentTotalSum, previousTotalSum), previousTotalSum, currentTotalSum);

var currentNewSum = current.Sum(c => c.NewCases);
var previousNewSum = previous.Sum(c => c.NewCases);
yield return new CountryVaryStatisticDto(ValueKeys.New,
CalculateVary(currentNewSum, previousNewSum), previousNewSum, currentNewSum);

var currentActiveSum = current.Sum(c => c.ActiveCases);
var previousActiveSum = previous.Sum(c => c.ActiveCases);
yield return new CountryVaryStatisticDto(ValueKeys.Active,
CalculateVary(currentActiveSum, previousActiveSum), previousActiveSum, currentActiveSum);

var currentDeathsSum = current.Sum(c => c.TotalDeaths);
var previousDeathsSum = previous.Sum(c => c.TotalDeaths);
yield return new CountryVaryStatisticDto(ValueKeys.Deaths,
CalculateVary(currentDeathsSum, previousDeathsSum), previousDeathsSum, currentDeathsSum);

var currentNewDeathsSum = current.Sum(c => c.NewDeaths);
var previousNewDeathsSum = previous.Sum(c => c.NewDeaths);
yield return new CountryVaryStatisticDto(ValueKeys.NewDeaths,
CalculateVary(currentNewDeathsSum, previousNewDeathsSum), previousNewDeathsSum, currentNewDeathsSum);

var currentRecoveredSum = current.Sum(c => c.RecoveredCases);
var previousRecoveredSum = previous.Sum(c => c.RecoveredCases);
yield return new CountryVaryStatisticDto(ValueKeys.Recovered,
CalculateVary(currentRecoveredSum, previousRecoveredSum), previousRecoveredSum, currentRecoveredSum);
}

private static double CalculateVary(int current, int previous)
{
return current > previous
? CalculateIncrease(current, previous)
: CalculateDecrease(current, previous);
}

private static double CalculateIncrease(int current, int previous)
{
var difference = current - previous;
return difference / (double) current * 100;
}

private static double CalculateDecrease(int current, int previous)
{
var difference = previous - current;
var decrease = difference / (double) previous * 100;

return decrease * -1;
}

private static CountryVaryStatisticContainerDto CreateEmpty(DateTime fetchedAt,
IReadOnlyList<CountryStatistic> currentStatistics)
{
return new(fetchedAt, new List<CountryVaryStatisticDto>
{
new(ValueKeys.Total, null, null, currentStatistics.Sum(c => c.TotalCases)),
new(ValueKeys.New, null, null, currentStatistics.Sum(c => c.NewCases)),
new(ValueKeys.Active, null, null, currentStatistics.Sum(c => c.ActiveCases)),
new(ValueKeys.Total, null, null, currentStatistics.Sum(c => c.TotalDeaths)),
new(ValueKeys.NewDeaths, null, null, currentStatistics.Sum(c => c.NewDeaths)),
new(ValueKeys.Recovered, null, null, currentStatistics.Sum(c => c.RecoveredCases)),
});
}
}
}
15 changes: 15 additions & 0 deletions src/Covid19Api.Services/Calculators/ValueKeys.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Covid19Api.Services.Tests")]
namespace Covid19Api.Services.Calculators
{
internal static class ValueKeys
{
public const string Total = "Total";
public const string New = "New";
public const string Active = "Active";
public const string Deaths = "Deaths";
public const string NewDeaths = "NewDeaths";
public const string Recovered = "Recovered";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using Covid19Api.Presentation.Response;
using Covid19Api.UseCases.Abstractions.Base;
using Covid19Api.UseCases.Abstractions.Models;
using MediatR;

namespace Covid19Api.UseCases.Abstractions.Queries.CountryStatistics
{
public record CalculateVaryForCountriesStatisticsQuery(DateTime MinFetchedAt) : ICacheableRequest, IRequest<IEnumerable<CountryVaryStatisticContainerDto>>
{
public CacheConfiguration GetCacheConfiguration() => new(nameof(CalculateVaryForCountriesStatisticsQuery), TimeSpan.FromMinutes(30));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using Covid19Api.Presentation.Response;
using Covid19Api.UseCases.Abstractions.Base;
using Covid19Api.UseCases.Abstractions.Models;
using MediatR;

namespace Covid19Api.UseCases.Abstractions.Queries.CountryStatistics
{
public record CalculateVaryForCountryStatisticsQuery(string Country, DateTime MinFetchedAt) : ICacheableRequest, IRequest<IEnumerable<CountryVaryStatisticContainerDto>>
{
public CacheConfiguration GetCacheConfiguration() => new(nameof(CalculateVaryForCountryStatisticsQuery), TimeSpan.FromMinutes(30));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Covid19Api.UseCases.Abstractions.Queries.CountryStatistics
{
public sealed record LoadHistoricalCountryStatisticsForCountryQuery(string Country) : ICacheableRequest,
public sealed record LoadHistoricalCountryStatisticsForCountryQuery(string Country, DateTime MinFetchedAt) : ICacheableRequest,
IRequest<IEnumerable<CountryStatisticDto>>
{
public CacheConfiguration GetCacheConfiguration() =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Covid19Api.Presentation.Response;
using Covid19Api.Repositories.Abstractions;
using Covid19Api.Services.Abstractions.Calculators;
using Covid19Api.UseCases.Abstractions.Queries.CountryStatistics;
using MediatR;

namespace Covid19Api.UseCases.Queries.CountryStatistics
{
public class CalculateVaryForCountriesStatisticsQueryHandler : IRequestHandler<CalculateVaryForCountriesStatisticsQuery, IEnumerable<CountryVaryStatisticContainerDto>>
{
private readonly ICountryStatisticsReadRepository countryStatisticsReadRepository;
private readonly ICountryVaryStatisticsCalculator countryVaryStatisticsCalculator;

public CalculateVaryForCountriesStatisticsQueryHandler(ICountryStatisticsReadRepository countryStatisticsReadRepository, ICountryVaryStatisticsCalculator countryVaryStatisticsCalculator)
{
this.countryStatisticsReadRepository = countryStatisticsReadRepository;
this.countryVaryStatisticsCalculator = countryVaryStatisticsCalculator;
}

public async Task<IEnumerable<CountryVaryStatisticContainerDto>> Handle(CalculateVaryForCountriesStatisticsQuery request, CancellationToken cancellationToken)
{
var historicalCountriesStatistics = await this.countryStatisticsReadRepository.HistoricalAsync(request.MinFetchedAt);

return countryVaryStatisticsCalculator.Calculate(historicalCountriesStatistics);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Covid19Api.Presentation.Response;
using Covid19Api.Repositories.Abstractions;
using Covid19Api.Services.Abstractions.Calculators;
using Covid19Api.UseCases.Abstractions.Queries.CountryStatistics;
using MediatR;

namespace Covid19Api.UseCases.Queries.CountryStatistics
{
public class CalculateVaryForCountryStatisticsQueryHandler : IRequestHandler<CalculateVaryForCountryStatisticsQuery, IEnumerable<CountryVaryStatisticContainerDto>>
{
private readonly ICountryStatisticsReadRepository countryStatisticsReadRepository;
private readonly ICountryVaryStatisticsCalculator countryVaryStatisticsCalculator;

public CalculateVaryForCountryStatisticsQueryHandler(ICountryStatisticsReadRepository countryStatisticsReadRepository, ICountryVaryStatisticsCalculator countryVaryStatisticsCalculator)
{
this.countryStatisticsReadRepository = countryStatisticsReadRepository;
this.countryVaryStatisticsCalculator = countryVaryStatisticsCalculator;
}

public async Task<IEnumerable<CountryVaryStatisticContainerDto>> Handle(CalculateVaryForCountryStatisticsQuery request, CancellationToken cancellationToken)
{
var historicalCountriesStatistics = await this.countryStatisticsReadRepository.HistoricalAsync(request.MinFetchedAt, request.Country);

return this.countryVaryStatisticsCalculator.Calculate(historicalCountriesStatistics);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ public async Task<IEnumerable<CountryStatisticDto>> Handle(
LoadHistoricalCountryStatisticsForCountryQuery request,
CancellationToken cancellationToken)
{
var minFetchedAt = DateTime.UtcNow.Date.AddDays(-9);

var statsForCountry = await this.countryStatisticsReadRepository.HistoricalAsync(minFetchedAt, request.Country);
var statsForCountry = await this.countryStatisticsReadRepository.HistoricalAsync(request.MinFetchedAt, request.Country);

return this.mapper.Map<IEnumerable<CountryStatisticDto>>(statsForCountry);
}
Expand Down
Loading

0 comments on commit 96c9704

Please sign in to comment.