Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions SIBR.Storage.API/Controllers/GameStatsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CsvHelper;
using Microsoft.AspNetCore.Mvc;
using NodaTime;
using Serilog;
using SIBR.Storage.API.Models;
using SIBR.Storage.API.Utils;
using SIBR.Storage.Data;
using SIBR.Storage.Data.Models;
using SIBR.Storage.Data.Query;

namespace SIBR.Storage.API.Controllers
{
[ApiController]
[ApiVersion("1.0")]
[Route("v{version:apiVersion}")]
public class GameStatsController : ControllerBase
{
private readonly GameStore _gameStore;
private readonly UpdateStore _store;

public GameStatsController(GameStore gameStore, UpdateStore store)
{
_gameStore = gameStore;
_store = store;
}

[Route("games/stats")]
public async Task<IActionResult> GetGameStats([FromQuery] GameStatsOptions opts)
{
var game = await _gameStore.GetGames(new GameStore.GameQueryOptions { GameId = opts.Game, Count = 1 }).FirstAsync();
var cutoff = CutoffTime(game.EndTime);

var gameStats = await GetVersion(UpdateType.GameStatsheet, new [] { game.Statsheet }, cutoff).FirstOrDefaultAsync();
if (gameStats is null)
return Ok(new ApiResponse<ApiGameStats>() { Data = new ApiGameStats[] {} });

var teamSheets = new [] { gameStats.Data.GetProperty("awayTeamStats").GetGuid(), gameStats.Data.GetProperty("homeTeamStats").GetGuid() };
var teamStats = await GetVersion(UpdateType.TeamStatsheet, teamSheets, cutoff).ToListAsync();

var playerSheets = teamStats.SelectMany(sheet => sheet.Data.GetProperty("playerStats").EnumerateArray().Select(el => el.GetGuid()));
var playerStats = await GetVersion(UpdateType.PlayerStatsheet, playerSheets.ToArray(), cutoff).ToListAsync();

return Ok(new ApiResponse<ApiGameStats>() { Data = new ApiGameStats[]
{
new ApiGameStats
{
GameId = game.GameId,
Timestamp = gameStats.Timestamp,
GameStats = gameStats.Data,
TeamStats = teamStats.Select(v => v.Data).ToArray(),
PlayerStats = playerStats.Select(v => v.Data).ToArray(),
}
}});
}

private Instant? CutoffTime(Instant? time)
{
if (time is null)
return null;

// We want the last recorded statsheet before the next game begins, so take the end time and round up to the next hour.
var dt = (time ?? Instant.MaxValue).InUtc();
var cutoff = dt.Date + TimeAdjusters.TruncateToHour(dt.TimeOfDay);
return cutoff.InUtc().ToInstant() + Duration.FromHours(1);
}

private IAsyncEnumerable<EntityUpdateView> GetVersion(UpdateType type, Guid[] ids, Instant? before)
{
return _store.ExportAllUpdatesRaw(type, new UpdateStore.EntityVersionQuery
{
Ids = ids,
Before = before,
Order = SortOrder.Desc,
Count = ids.Length,
});
}

public class GameStatsOptions
{
public Guid Game { get; set; }
}
}
}
16 changes: 16 additions & 0 deletions SIBR.Storage.API/Models/ApiGameStats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Text.Json;
using NodaTime;
using SIBR.Storage.Data.Models;

namespace SIBR.Storage.API.Models
{
public class ApiGameStats
{
public Guid GameId { get; set; }
public Instant Timestamp { get; set; }
public JsonElement GameStats { get; set; }
public JsonElement[] TeamStats { get; set; }
public JsonElement[] PlayerStats { get; set; }
}
}
1 change: 1 addition & 0 deletions SIBR.Storage.Data/Models/Read/GameView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace SIBR.Storage.Data.Models
public class GameView: IGameData
{
public Guid GameId { get; set; }
public Guid Statsheet { get; set; }
public Instant? StartTime { get; set; }
public Instant? EndTime { get; set; }
public JsonElement Data { get; set; }
Expand Down
1 change: 1 addition & 0 deletions SIBR.Storage.Data/Schema/r__games_view.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ create view games_view as
(jsonb_array_length(data->'outcomes') > 0) as has_outcomes,
(data->>'gameStart')::bool as has_started,
(data->>'gameComplete')::bool as has_finished,
(data->>'statsheet')::uuid as statsheet,
(data->>'homeTeam')::uuid as home_team,
(data->>'awayTeam')::uuid as away_team,
(data->>'homePitcher')::uuid as home_pitcher,
Expand Down
2 changes: 2 additions & 0 deletions SIBR.Storage.Data/Stores/GameStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public IAsyncEnumerable<GameView> GetGames(GameQueryOptions opts)
else
q.OrderBy("season", "tournament", "day");

if (opts.GameId != null) q.Where("game_id", opts.GameId.Value);
if (opts.Season != null) q.Where("season", opts.Season.Value);
if (opts.Tournament != null) q.Where("tournament", opts.Tournament.Value);
if (opts.Day != null) q.Where("day", opts.Day.Value);
Expand All @@ -43,6 +44,7 @@ public IAsyncEnumerable<GameView> GetGames(GameQueryOptions opts)

public class GameQueryOptions
{
public Guid? GameId;
public int? Tournament;
public int? Season;
public int? Day;
Expand Down