< Summary

Information
Class: Orchestrator.Commands.Operations.Wm26RecentHistory.Wm26RecentHistoryExportDateMapCommand
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Operations/Wm26RecentHistory/Wm26RecentHistoryExportDateMapCommand.cs
Line coverage
90%
Covered lines: 79
Uncovered lines: 8
Coverable lines: 87
Total lines: 163
Line coverage: 90.8%
Branch coverage
93%
Covered branches: 30
Total branches: 32
Branch coverage: 93.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ExecuteAsync()92.86%292888.71%
ReadExistingEntries()100%22100%
IsRecentHistoryDocument(...)100%22100%
CreateDateMapKey(...)100%11100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Operations/Wm26RecentHistory/Wm26RecentHistoryExportDateMapCommand.cs

#LineLine coverage
 1using EHonda.KicktippAi.Core;
 2using Microsoft.Extensions.Logging;
 3using Orchestrator.Infrastructure;
 4using Orchestrator.Infrastructure.Factories;
 5using Spectre.Console;
 6using Spectre.Console.Cli;
 7
 8namespace Orchestrator.Commands.Operations.Wm26RecentHistory;
 9
 10public sealed class Wm26RecentHistoryExportDateMapCommand
 11    : AsyncCommand<Wm26RecentHistoryExportDateMapSettings>
 12{
 13    private readonly IAnsiConsole _console;
 14    private readonly IFirebaseServiceFactory _firebaseServiceFactory;
 15    private readonly ILogger<Wm26RecentHistoryExportDateMapCommand> _logger;
 16
 117    public Wm26RecentHistoryExportDateMapCommand(
 118        IAnsiConsole console,
 119        IFirebaseServiceFactory firebaseServiceFactory,
 120        ILogger<Wm26RecentHistoryExportDateMapCommand> logger)
 21    {
 122        _console = console;
 123        _firebaseServiceFactory = firebaseServiceFactory;
 124        _logger = logger;
 125    }
 26
 27    protected override async Task<int> ExecuteAsync(
 28        CommandContext context,
 29        Wm26RecentHistoryExportDateMapSettings settings,
 30        CancellationToken cancellationToken)
 31    {
 32        try
 33        {
 134            var competition = CompetitionResolver.ResolveCompetition(
 135                settings.Competition,
 136                communityContext: settings.CommunityContext);
 137            var repositoryCompetition = CompetitionResolver.ToRepositoryCompetitionArgument(competition);
 138            var contextRepository = _firebaseServiceFactory.CreateContextRepository(repositoryCompetition);
 39
 140            _console.MarkupLine($"[green]Exporting WM26 recent-history date map for:[/] [yellow]{settings.CommunityConte
 141            _console.MarkupLine($"[blue]Using competition:[/] [yellow]{competition}[/]");
 42
 143            var existingEntries = await ReadExistingEntries(settings.Output, cancellationToken);
 144            var existingByKey = existingEntries
 145                .GroupBy(CreateDateMapKey, StringComparer.OrdinalIgnoreCase)
 146                .ToDictionary(
 147                    group => group.Key,
 148                    group => new Queue<HistoryDateMapEntry>(group),
 149                    StringComparer.OrdinalIgnoreCase);
 150            var exportedEntries = new List<HistoryDateMapEntry>();
 151            var preservedCount = 0;
 52
 153            var documentNames = await contextRepository.GetContextDocumentNamesAsync(
 154                settings.CommunityContext,
 155                cancellationToken);
 156            var historyDocumentNames = documentNames
 157                .Where(IsRecentHistoryDocument)
 058                .OrderBy(name => name, StringComparer.Ordinal)
 159                .ToList();
 60
 161            if (historyDocumentNames.Count == 0)
 62            {
 063                _console.MarkupLine("[yellow]No recent-history documents found[/]");
 064                return 1;
 65            }
 66
 167            foreach (var documentName in historyDocumentNames)
 68            {
 169                var document = await contextRepository.GetLatestContextDocumentAsync(
 170                    documentName,
 171                    settings.CommunityContext,
 172                    cancellationToken);
 173                if (document is null)
 74                {
 75                    continue;
 76                }
 77
 178                var documentEntries = HistoryCsvUtility.ExtractDateMapEntries(documentName, document.Content);
 179                foreach (var entry in documentEntries)
 80                {
 181                    if (existingByKey.TryGetValue(CreateDateMapKey(entry), out var existingEntriesForRow) &&
 182                        existingEntriesForRow.TryDequeue(out var existing))
 83                    {
 184                        exportedEntries.Add(entry with
 185                        {
 186                            PlayedAt = existing.PlayedAt,
 187                            SourceName = existing.SourceName,
 188                            SourceUrl = existing.SourceUrl,
 189                            VerifiedAt = existing.VerifiedAt,
 190                            Notes = existing.Notes
 191                        });
 92
 193                        if (!string.IsNullOrWhiteSpace(existing.PlayedAt))
 94                        {
 195                            preservedCount++;
 96                        }
 97                    }
 98                    else
 99                    {
 1100                        exportedEntries.Add(entry);
 101                    }
 102                }
 103
 1104                if (settings.Verbose)
 105                {
 0106                    _console.MarkupLine($"[dim]  Exported {documentEntries.Count} row(s) from {documentName}[/]");
 107                }
 1108            }
 109
 1110            var outputDirectory = Path.GetDirectoryName(settings.Output);
 1111            if (!string.IsNullOrWhiteSpace(outputDirectory))
 112            {
 1113                Directory.CreateDirectory(outputDirectory);
 114            }
 115
 1116            await File.WriteAllTextAsync(
 1117                settings.Output,
 1118                HistoryCsvUtility.WriteDateMapEntries(exportedEntries),
 1119                cancellationToken);
 120
 1121            _console.MarkupLine($"[green]Exported {exportedEntries.Count} date-map row(s) to {settings.Output}[/]");
 1122            _console.MarkupLine($"[dim]Preserved {preservedCount} existing played date(s)[/]");
 123
 1124            return 0;
 125        }
 0126        catch (Exception ex)
 127        {
 0128            _logger.LogError(ex, "Failed to export WM26 recent-history date map");
 0129            _console.MarkupLine($"[red]Error:[/] {ex.Message}");
 0130            return 1;
 131        }
 1132    }
 133
 134    private static async Task<IReadOnlyList<HistoryDateMapEntry>> ReadExistingEntries(
 135        string path,
 136        CancellationToken cancellationToken)
 137    {
 1138        if (!File.Exists(path))
 139        {
 1140            return Array.Empty<HistoryDateMapEntry>();
 141        }
 142
 1143        var content = await File.ReadAllTextAsync(path, cancellationToken);
 1144        return HistoryCsvUtility.ReadDateMapEntries(content);
 1145    }
 146
 147    private static bool IsRecentHistoryDocument(string documentName)
 148    {
 1149        return documentName.StartsWith("recent-history-", StringComparison.OrdinalIgnoreCase)
 1150               && documentName.EndsWith(".csv", StringComparison.OrdinalIgnoreCase);
 151    }
 152
 153    private static string CreateDateMapKey(HistoryDateMapEntry entry)
 154    {
 1155        return string.Join('|',
 1156            entry.DocumentName.Trim(),
 1157            entry.Competition.Trim(),
 1158            entry.HomeTeam.Trim(),
 1159            entry.AwayTeam.Trim(),
 1160            entry.Score.Trim(),
 1161            entry.Annotation.Trim());
 162    }
 163}