This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.12" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LaDOSE.Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// One spreadsheet write: the tables to put in it, and which spreadsheet.
|
||||
/// Not an entity — never mapped by EF. It arrives as SheetExportRequestDTO and is
|
||||
/// mapped by AutoMapper, same pattern as <see cref="MatchStats"/>, so property names
|
||||
/// must stay identical to the DTO's.
|
||||
///
|
||||
/// The tables are built client-side by $lib/tournaments/cumulative.ts, which reuses the
|
||||
/// very same buildRanking() that produces the CSV export. That is deliberate: the sheet
|
||||
/// and the CSV cannot drift because one function produces both.
|
||||
/// </summary>
|
||||
public class SheetExportRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolved by SheetsExportService from configuration — never supplied by the caller,
|
||||
/// so a client cannot aim the export at someone else's spreadsheet.
|
||||
/// </summary>
|
||||
public string SpreadsheetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// One tab per selected event, oldest first. Tab N holds events 1..N merged, so the
|
||||
/// last tab is the full cumulative ranking for the scope.
|
||||
/// </summary>
|
||||
public List<SheetTable> Tabs { get; set; } = new List<SheetTable>();
|
||||
}
|
||||
|
||||
/// <summary>One tab: a players x games grid with a Total column.</summary>
|
||||
public class SheetTable
|
||||
{
|
||||
/// <summary>Desired tab title, e.g. "Ranking #1301". Sanitised before use.</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Provenance and logging only. Tabs are addressed by title, never by this — see the
|
||||
/// note on tab identity in SheetsExportService.
|
||||
/// </summary>
|
||||
public int EventId { get; set; }
|
||||
|
||||
/// <summary>["Players", <game names...>, "Total"].</summary>
|
||||
public List<string> Header { get; set; } = new List<string>();
|
||||
|
||||
public List<SheetRow> Rows { get; set; } = new List<SheetRow>();
|
||||
|
||||
/// <summary>
|
||||
/// Written below the grid, one line per entry, blank row in between. Carries which
|
||||
/// events went into this tab so a stale or incomplete tab identifies itself.
|
||||
/// </summary>
|
||||
public List<string> Footer { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
public class SheetRow
|
||||
{
|
||||
public string Player { get; set; }
|
||||
|
||||
/// <summary>Index-aligned with Header[1..^1] — one entry per game column.</summary>
|
||||
public List<int> Points { get; set; } = new List<int>();
|
||||
|
||||
public int Total { get; set; }
|
||||
}
|
||||
|
||||
public class SheetExportResult
|
||||
{
|
||||
public string SpreadsheetId { get; set; }
|
||||
public string SpreadsheetUrl { get; set; }
|
||||
|
||||
/// <summary>Which ISheetsWriter did the work — "ServiceAccount", "Logging", ...</summary>
|
||||
public string Writer { get; set; }
|
||||
|
||||
public List<SheetTabResult> Tabs { get; set; } = new List<SheetTabResult>();
|
||||
|
||||
/// <summary>Anything the user should know but that did not stop the write, e.g. renames.</summary>
|
||||
public List<string> Warnings { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
public class SheetTabResult
|
||||
{
|
||||
/// <summary>The title as asked for, before sanitisation.</summary>
|
||||
public string RequestedName { get; set; }
|
||||
|
||||
/// <summary>The title actually written to.</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Rows { get; set; }
|
||||
public int Columns { get; set; }
|
||||
|
||||
/// <summary>False when an existing tab of that name was rewritten in place.</summary>
|
||||
public bool Created { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>What the UI needs to render the export panel. Never carries a secret.</summary>
|
||||
public class SheetsConfig
|
||||
{
|
||||
public string Writer { get; set; }
|
||||
|
||||
/// <summary>The writer has its credentials and a target spreadsheet is configured.</summary>
|
||||
public bool Configured { get; set; }
|
||||
|
||||
public string SpreadsheetId { get; set; }
|
||||
public int MaxTabs { get; set; }
|
||||
public int MaxRowsPerTab { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-side limits and the target spreadsheet, read from the GoogleSheets configuration
|
||||
/// section. Constructor-injected rather than IOptions, matching how the Challonge and Smash
|
||||
/// providers take their keys.
|
||||
/// </summary>
|
||||
public class SheetsSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The spreadsheet everything is written to. Reset each year — see .env.example.
|
||||
/// </summary>
|
||||
public string SpreadsheetId { get; set; }
|
||||
|
||||
public int MaxTabs { get; set; } = 60;
|
||||
public int MaxRowsPerTab { get; set; } = 5000;
|
||||
|
||||
/// <summary>Google caps a tab at 100 chars; this caps the grid width.</summary>
|
||||
public int MaxColumns { get; set; } = 200;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LaDOSE.Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// Every recorded meeting between two players, broken down per game.
|
||||
/// Not an entity: computed in memory by StatisticsService and mapped to
|
||||
/// PlayerVersusDTO by AutoMapper, same as <see cref="MatchStats"/>.
|
||||
/// Property names must stay identical to the DTO's, the mapping is by convention.
|
||||
///
|
||||
/// A <see cref="Set"/> carries no game of its own — the game comes from the
|
||||
/// <see cref="Tournament"/> the set belongs to, and that Tournament.GameId is
|
||||
/// nullable. Sets behind a bracket with no game are counted in
|
||||
/// <see cref="UnknownGameSets"/> and excluded from everything else, because a
|
||||
/// per-game breakdown cannot say anything about them.
|
||||
/// </summary>
|
||||
public class PlayerVersus
|
||||
{
|
||||
public int PlayerAId { get; set; }
|
||||
|
||||
/// <summary>Gamertag, falling back to Name, else "#<id>".</summary>
|
||||
public string PlayerA { get; set; }
|
||||
|
||||
public int PlayerBId { get; set; }
|
||||
|
||||
public string PlayerB { get; set; }
|
||||
|
||||
/// <summary>Meetings in a bracket whose game is known — the sum of the rows below.</summary>
|
||||
public int Sets { get; set; }
|
||||
|
||||
/// <summary>Of those, the ones with a determinable winner (unequal scores).</summary>
|
||||
public int DecidedSets { get; set; }
|
||||
|
||||
public int WinsA { get; set; }
|
||||
public int WinsB { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Meetings dropped because the bracket has no game attached. Reported rather
|
||||
/// than hidden: it is the difference between "they never met" and "we cannot
|
||||
/// tell which game they met in".
|
||||
/// </summary>
|
||||
public int UnknownGameSets { get; set; }
|
||||
|
||||
/// <summary>One row per game they actually met in, most-played first.</summary>
|
||||
public List<VersusGameStats> Games { get; set; } = new List<VersusGameStats>();
|
||||
}
|
||||
|
||||
/// <summary>One game's slice of a <see cref="PlayerVersus"/>.</summary>
|
||||
public class VersusGameStats
|
||||
{
|
||||
public int GameId { get; set; }
|
||||
|
||||
/// <summary>Game.Name, or "#<id>" when the game row is gone.</summary>
|
||||
public string Game { get; set; }
|
||||
|
||||
public string GameLongName { get; set; }
|
||||
|
||||
/// <summary>Meetings in this game, decided or not.</summary>
|
||||
public int Sets { get; set; }
|
||||
|
||||
/// <summary>Meetings with a determinable winner. Always equals WinsA + WinsB.</summary>
|
||||
public int DecidedSets { get; set; }
|
||||
|
||||
public int WinsA { get; set; }
|
||||
public int WinsB { get; set; }
|
||||
|
||||
/// <summary>Individual games won inside the decided sets (a DQ, stored as -1, clamps to 0).</summary>
|
||||
public int GamesWonA { get; set; }
|
||||
|
||||
public int GamesWonB { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A player who can be picked for a versus lookup, i.e. one with at least one set
|
||||
/// in a bracket whose game is known. Offering anyone else would mean offering
|
||||
/// players the breakdown must then report as empty.
|
||||
/// </summary>
|
||||
public class PlayerOption
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>Gamertag, falling back to Name, else "#<id>".</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>Sets in a bracket with a known game, whoever the opponent was.</summary>
|
||||
public int Sets { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user