Skip to content

Commit

Permalink
feat: fetch most recent data from db rather than using the html document
Browse files Browse the repository at this point in the history
  • Loading branch information
alsami committed Feb 20, 2021
1 parent b5af2e0 commit 45b40d5
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 157 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ While `worldometers` is only displaying recent data, this API is providing histo
The API is hosted on azure using an app-service. All times returned from the server are in UTC.

Api base url:
https://app-covid-19-statistics-api.azurewebsites.net
https://api.alsami-covid19-statistics.dev/

A swagger definition can be found [here](https://api.alsami-covid19-statistics.dev/swagger/index.html) for testing the API.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public CountryStatisticsController(IMediator mediator)

[HttpGet]
public Task<IEnumerable<CountryStatisticDto>> LoadLatestAsync() =>
this.mediator.Send(new LoadLatestCountriesStatisticsQuery());
this.mediator.Send(new LoadCurrentStatisticsForCountyQuery());

[HttpGet("history")]
public Task<IEnumerable<CountryStatisticDto>> LoadHistoryAsync()
Expand All @@ -34,7 +34,7 @@ public Task<IEnumerable<CountryVaryStatisticContainerDto>> LoadVaryHistoryAsync(

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

[HttpGet("{country}/history")]
public Task<IEnumerable<CountryStatisticDto>> LoadHistoryForCountryAsync(string country) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected override async Task ExecuteAsync()
if (end.Year < next.Year)
break;

var loadCountriesStatisticsQuery = new LoadLatestCountriesStatisticsQuery();
var loadCountriesStatisticsQuery = new LoadCurrentStatisticsForCountyQuery();
var countries = (await this.mediator.Send(loadCountriesStatisticsQuery))
.Select(country => country.Country).ToList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ namespace Covid19Api.Repositories.Abstractions
{
public interface ICountryStatisticsReadRepository
{
Task<CountryStatistic> MostRecentAsync(string country);
Task<IEnumerable<CountryStatistic>> HistoricalAsync(DateTime minFetchedAt);
Task<IEnumerable<CountryStatistic>> HistoricalAsync(DateTime minFetchedAt, string country);
Task<IEnumerable<CountryStatistic>> HistoricalForDayAsync(DateTime minFetchedAt, string country);

Task<CountryStatistic?> FindInRangeAsync(string country, DateTime inclusiveStart,
DateTime exclusiveEnd);

Task<CountryStatistic> LoadCurrentAsync(string country);

Task<IEnumerable<CountryStatistic>> LoadCurrentAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ namespace Covid19Api.Repositories.Abstractions
{
public interface IGlobalStatisticsReadRepository
{
Task<GlobalStatistics> LoadCurrentAsync();

Task<IEnumerable<GlobalStatistics>> HistoricalAsync(DateTime minFetchedAt);
Task<IEnumerable<GlobalStatistics>> HistoricalForDayAsync(DateTime minFetchedAt);
Task<GlobalStatistics?> FindInRangeAsync(DateTime inclusiveStart, DateTime inclusiveEnd);
}
}
67 changes: 34 additions & 33 deletions src/Covid19Api.Repositories/CountryStatisticsReadRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,6 @@ public CountryStatisticsReadRepository(Covid19ApiDbContext context)
this.context = context;
}

public async Task<CountryStatistic> MostRecentAsync(string country)
{
var collection = this.GetCollection();

var sort = Builders<CountryStatistic>.Sort
.Descending(nameof(CountryStatistic.TotalCases))
.Descending(nameof(CountryStatistic.FetchedAt));

// ReSharper disable once SpecifyStringComparison
var cursor = await collection.FindAsync(statistics =>
statistics.FetchedAt >= DateTime.UtcNow.Date.AddDays(-1) &&
statistics.Country.ToLower() == country.ToLower() &&
statistics.Key == EntityKeys.CountryStatistics,
new FindOptions<CountryStatistic>
{
Sort = sort,
});

return await cursor.FirstOrDefaultAsync();
}

public async Task<IEnumerable<CountryStatistic>> HistoricalAsync(DateTime minFetchedAt)
{
var collection = this.GetCollection();
Expand Down Expand Up @@ -90,18 +69,6 @@ public async Task<IEnumerable<CountryStatistic>> HistoricalAsync(DateTime minFet
return await cursor.ToListAsync();
}

public async Task<IEnumerable<CountryStatistic>> HistoricalForDayAsync(DateTime minFetchedAt, string country)
{
var collection = this.GetCollection();

var cursor = await collection.FindAsync(
statistics => statistics.FetchedAt >= minFetchedAt &&
statistics.Country.ToLowerInvariant() == country.ToLowerInvariant() &&
statistics.Key == EntityKeys.CountryStatistics);

return await cursor.ToListAsync();
}

public async Task<CountryStatistic?> FindInRangeAsync(string country, DateTime inclusiveStart,
DateTime exclusiveEnd)
{
Expand Down Expand Up @@ -135,6 +102,40 @@ public async Task<IEnumerable<CountryStatistic>> HistoricalForDayAsync(DateTime
return await cursor.FirstOrDefaultAsync();
}

public Task<CountryStatistic> LoadCurrentAsync(string country)
{
var keyFilter =
Builders<CountryStatistic>.Filter.Where(statistics =>
statistics.Key == EntityKeys.CountryStatistics);

var countryFilter =
Builders<CountryStatistic>.Filter.Where(statistic => statistic.Country.ToLower() == country.ToLower());

var filter = keyFilter & countryFilter;

return this.GetCollection()
.Find(filter)
.SortByDescending(statistic => statistic.FetchedAt)
.Limit(1)
.SingleAsync();
}

public async Task<IEnumerable<CountryStatistic>> LoadCurrentAsync()
{
var collection = this.GetCollection();

var cursor = await collection
.Find(statistic => statistic.FetchedAt >= DateTime.UtcNow.Date.AddDays(-1) && statistic.Key == EntityKeys.CountryStatistics)
.SortByDescending(statistic => statistic.TotalCases)
.ToListAsync();

var onlyLatestEntries = cursor
.GroupBy(countryStats => countryStats.Country)
.SelectMany(grouping => grouping.Take(1));

return onlyLatestEntries;
}

private IMongoCollection<CountryStatistic> GetCollection()
=> this.context.Database.GetCollection<CountryStatistic>(CollectionNames.CountryStatistics);
}
Expand Down
22 changes: 9 additions & 13 deletions src/Covid19Api.Repositories/GlobalStatisticsReadRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ public GlobalStatisticsReadRepository(Covid19ApiDbContext context)
this.context = context;
}

public Task<GlobalStatistics> LoadCurrentAsync()
{
var collection = this.GetCollection();

var keyFilter = Builders<GlobalStatistics>.Filter.Where(global => global.Key == EntityKeys.GlobalStatistics);

return collection.Find(keyFilter).SortByDescending(statistics => statistics.FetchedAt).Limit(1).SingleAsync();
}

public async Task<IEnumerable<GlobalStatistics>> HistoricalAsync(DateTime minFetchedAt)
{
var collection = this.GetCollection();
Expand All @@ -38,19 +47,6 @@ public async Task<IEnumerable<GlobalStatistics>> HistoricalAsync(DateTime minFet
return await cursor.ToListAsync();
}

public async Task<IEnumerable<GlobalStatistics>> HistoricalForDayAsync(DateTime minFetchedAt)
{
var collection = this.GetCollection();

var cursor = await collection.FindAsync(
globalStatistics => globalStatistics.FetchedAt >= minFetchedAt &&
globalStatistics.Key == EntityKeys.GlobalStatistics);

var all = await cursor.ToListAsync();

return all.OrderBy(entry => entry.FetchedAt);
}

public async Task<GlobalStatistics?> FindInRangeAsync(DateTime inclusiveStart, DateTime inclusiveEnd)
{
var collection = this.GetCollection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

namespace Covid19Api.UseCases.Abstractions.Queries.CountryStatistics
{
public sealed record LoadLatestStatisticsForCountryQuery(string Country) : ICacheableRequest, IRequest<CountryStatisticDto>
public sealed record LoadCurrentStatisticsForCountryQuery(string Country) : ICacheableRequest, IRequest<CountryStatisticDto>
{
public CacheConfiguration GetCacheConfiguration() =>
new CacheConfiguration($"{nameof(LoadLatestStatisticsForCountryQuery)}_{this.Country}",
new CacheConfiguration($"{nameof(LoadCurrentStatisticsForCountryQuery)}_{this.Country}",
TimeSpan.FromMinutes(30));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace Covid19Api.UseCases.Abstractions.Queries.CountryStatistics
{
public sealed record LoadLatestCountriesStatisticsQuery : ICacheableRequest, IRequest<IEnumerable<CountryStatisticDto>>
public sealed record LoadCurrentStatisticsForCountyQuery : ICacheableRequest, IRequest<IEnumerable<CountryStatisticDto>>
{
public CacheConfiguration GetCacheConfiguration() =>
new CacheConfiguration(nameof(LoadLatestCountriesStatisticsQuery), TimeSpan.FromMinutes(30));
new CacheConfiguration(nameof(LoadCurrentStatisticsForCountyQuery), TimeSpan.FromMinutes(30));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using Covid19Api.Presentation.Response;
using Covid19Api.Repositories.Abstractions;
using Covid19Api.Services.Abstractions.Loader;
using Covid19Api.UseCases.Abstractions.Queries.CountryStatistics;
using Covid19Api.UseCases.Filter;
using MediatR;

namespace Covid19Api.UseCases.Queries.CountryStatistics
{
public class
LoadCurrentStatisticsForCountryQueryHandler : IRequestHandler<LoadCurrentStatisticsForCountryQuery,
CountryStatisticDto>
{
private readonly IMapper mapper;
private readonly ICountryStatisticsReadRepository countryStatisticsReadRepository;

public LoadCurrentStatisticsForCountryQueryHandler(IMapper mapper, ICountryStatisticsReadRepository countryStatisticsReadRepository)
{
this.mapper = mapper;
this.countryStatisticsReadRepository = countryStatisticsReadRepository;
}


public async Task<CountryStatisticDto> Handle(LoadCurrentStatisticsForCountryQuery request,
CancellationToken cancellationToken)
{
var current = await this.countryStatisticsReadRepository.LoadCurrentAsync(request.Country);

return this.mapper.Map<CountryStatisticDto>(current);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using Covid19Api.Presentation.Response;
using Covid19Api.Repositories.Abstractions;
using Covid19Api.UseCases.Abstractions.Queries.CountryStatistics;
using MediatR;

namespace Covid19Api.UseCases.Queries.CountryStatistics
{
public class LoadCurrentStatisticsForCountyQueryHandler : IRequestHandler<LoadCurrentStatisticsForCountyQuery, IEnumerable<CountryStatisticDto>>
{
private readonly IMapper mapper;
private readonly ICountryStatisticsReadRepository countryStatisticsReadRepository;


public LoadCurrentStatisticsForCountyQueryHandler(IMapper mapper, ICountryStatisticsReadRepository countryStatisticsReadRepository)
{
this.mapper = mapper;
this.countryStatisticsReadRepository = countryStatisticsReadRepository;
}

public async Task<IEnumerable<CountryStatisticDto>> Handle(LoadCurrentStatisticsForCountyQuery request,
CancellationToken cancellationToken)
{
var current = await this.countryStatisticsReadRepository.LoadCurrentAsync();

return this.mapper.Map<IEnumerable<CountryStatisticDto>>(current).OrderByDescending(country => country!.TotalCases);
}
}
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit 45b40d5

Please sign in to comment.