public async Task ExportSummaryCommandAsync()
        {
            using (Context.Channel.EnterTypingState())
            {
                var scoreboardTask = ScoreRetrievalService.GetScoreboardAsync(ScoreboardFilterInfo.NoFilter);

                var targetWriter = new System.IO.StringWriter();
                await targetWriter.WriteLineAsync("TeamId,Division,Category,Location,Tier,ImageCount,PlayTime,Score,Warnings").ConfigureAwait(false);

                CompleteScoreboardSummary scoreboard = await scoreboardTask.ConfigureAwait(false);

                foreach (var team in scoreboard.TeamList)
                {
                    await targetWriter.WriteLineAsync($"{team.TeamId},{team.Division.ToStringCamelCaseToSpace()},{(!team.Category.HasValue ? string.Empty : team.Category.Value.ToCanonicalName())},{team.Location},{(team.Tier.HasValue ? team.Tier.Value.ToString() : string.Empty)},{team.ImageCount},{team.PlayTime.ToHoursMinutesSecondsString()},{ScoreRetrievalService.Metadata.FormattingOptions.FormatScore(team.TotalScore)},{team.Warnings.ToConciseString()}").ConfigureAwait(false);
                }

                TimeZoneInfo tz = await Preferences.GetTimeZoneAsync(Context.Guild, Context.User).ConfigureAwait(false);

                string         tzAbbr            = tz.GetAbbreviations().Generic;
                DateTimeOffset snapshotTimestamp = TimeZoneInfo.ConvertTime(scoreboard.SnapshotTimestamp, tz);
                using (var targetStream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(targetWriter.GetStringBuilder().ToString())))
                {
                    await Context.Channel.SendFileAsync(targetStream, "scoreboard.csv", $"Scoreboard summary CSV export\nScore timestamp: {snapshotTimestamp:g} {tzAbbr}\nExported: {TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tz):g} {tzAbbr}").ConfigureAwait(false);
                }
            }
        }
            public async Task DownloadScoreboardAsync([Summary("A URL pointing to an existing GZip-compressed JSON backup.")] string existingDataUrl = null)
            {
                await ReplyAsync("Downloading scoreboard...").ConfigureAwait(false);


                using (Context.Channel.EnterTypingState())
                {
                    IReadOnlyDictionary <TeamId, ScoreboardDetails> existingArchive = null;
                    var retState = new ScoreboardDownloadService.ReturnedStateInfoWrapper();

                    try
                    {
                        string attachmentUrl = Context.Message?.Attachments?.FirstOrDefault()?.Url ?? existingDataUrl;
                        if (attachmentUrl != null)
                        {
                            using (var downloader = new HttpClient())
                                using (var unzipStream = new GZipStream(await downloader.GetStreamAsync(attachmentUrl).ConfigureAwait(false), CompressionMode.Decompress))
                                {
                                    existingArchive = new JsonScoreRetrievalService(await new StreamReader(unzipStream).ReadToEndAsync().ConfigureAwait(false)).StoredTeamDetails;
                                }
                        }
                    }
                    catch { }

                    using (MemoryStream ms = await ScoreDownloader.DownloadFullScoreboardGzippedAsync(retState: retState, previousScoreStatus: existingArchive).ConfigureAwait(false))
                    {
                        string fileName = $"scoreboard-{retState.Summary.SnapshotTimestamp.ToUnixTimeSeconds()}.json.gz";
                        if (Directory.Exists("scoreboardarchives"))
                        {
                            using (var fileStream = File.Create(Path.Combine("scoreboardarchives", fileName)))
                            {
                                await ms.CopyToAsync(fileStream).ConfigureAwait(false);
                            }
                        }
                        ms.Position = 0;
                        TimeZoneInfo tz = await Preferences.GetTimeZoneAsync(Context.Guild, Context.User).ConfigureAwait(false);

                        DateTimeOffset timestamp = TimeZoneInfo.ConvertTime(retState.Summary.SnapshotTimestamp, tz);

                        double downloadPercentSuccess = retState.DownloadTasks.Length == 0 ? 100 : (100.0 * retState.DownloadTasks.Count(t => t.IsCompletedSuccessfully)) / retState.DownloadTasks.Length;

                        await Context.Channel.SendFileAsync(ms, fileName,
                                                            $"JSON scoreboard snapshot for {timestamp:g} {tz.GetAbbreviations().Generic}\n" +
                                                            $"{Utilities.Pluralize("team", retState.TeamDetailCount)} total:\n" +
                                                            $"{Utilities.Pluralize("team", retState.DownloadTasks.Length)} downloaded from \"{ScoreService.Metadata.StaticSummaryLine}\"\n" +
                                                            $"{downloadPercentSuccess:F1}% of downloads successful").ConfigureAwait(false);
                    }
                }
            }
            public async Task GetStatus()
            {
                var state = ScoreDownloader.GetState();

                // list might be being modified, but array won't be
                // statuses might change during execution but Task itself is threadsafe

                if (state.OriginalTaskList == null)
                {
                    await ReplyAsync("__Status:__\nInitializing...").ConfigureAwait(false);

                    return;
                }

                // second might be higher because tasks continue executing during this
                int completedCount = state.OriginalTaskList.Count(t => t.IsCompleted);
                int faultedCount   = state.OriginalTaskList.Count(t => t.IsFaulted);

                TimeZoneInfo userTz = await Preferences.GetTimeZoneAsync(Context.Guild, Context.User).ConfigureAwait(false);

                TimeSpan elapsed = DateTimeOffset.UtcNow - state.StartTime;

                await ReplyAsync("__Status:__\n" +
                                 $"Started: {TimeZoneInfo.ConvertTime(state.StartTime, userTz):g} {userTz.GetAbbreviations().Generic} ({elapsed.ToLongString()} ago)\n" +
                                 $"Team downloads completed: {completedCount} / {state.OriginalTaskList.Length} ({(100.0 * completedCount) / state.OriginalTaskList.Length:F1}%)\n" +
                                 $"Team downloads errored: {faultedCount}\n" +
                                 $"About {(elapsed * Math.Min(state.OriginalTaskList.Length / (1.0*(completedCount + faultedCount)), 100000000) - elapsed).ToLongString(showSeconds: false)} remaining").ConfigureAwait(false);
            }
示例#4
0
            public async Task GetStatus()
            {
                var state = ScoreDownloader.GetState();

                // list might be being modified, but array won't be
                // statuses might change during execution but Task itself is threadsafe

                if (state.OriginalTaskList == null)
                {
                    await ReplyAsync("__Status:__\nInitializing...").ConfigureAwait(false);

                    return;
                }

                // second might be higher because tasks continue executing during this
                int completedCount = state.OriginalTaskList.Count(t => t.IsCompleted);

                TeamId[] faulted = state.OriginalTaskList.Select((t, i) => new { Id = state.OriginalDownloadList[i], Task = t }).Where(x => x.Task.IsFaulted).Select(x => x.Id).ToArray();

                TimeZoneInfo userTz = await Preferences.GetTimeZoneAsync(Context.Guild, Context.User).ConfigureAwait(false);

                TimeSpan elapsed = DateTimeOffset.UtcNow - state.StartTime;

                var faultedTeamsStatus = new StringBuilder();

                if (faulted.Length > 0)
                {
                    faultedTeamsStatus.AppendLine();
                    faultedTeamsStatus.AppendLine();
                    faultedTeamsStatus.AppendLine("__Teams whose downloads failed:__");
                    const int maxFaultedToDisplay = 20;
                    int       cap = Math.Min(faulted.Length, maxFaultedToDisplay);
                    for (int i = 0; i < cap; i++)
                    {
                        faultedTeamsStatus.AppendLine(faulted[i].ToString());
                    }

                    if (faulted.Length > maxFaultedToDisplay)
                    {
                        faultedTeamsStatus.AppendLine($"(and {faulted.Length - maxFaultedToDisplay} others)");
                    }
                }

                await ReplyAsync("__Status:__\n" +
                                 $"Started: {TimeZoneInfo.ConvertTime(state.StartTime, userTz):g} {userTz.GetAbbreviations().Generic} ({elapsed.ToLongString()} ago)\n" +
                                 $"Team downloads completed: {completedCount} / {state.OriginalTaskList.Length} ({(100.0 * completedCount) / state.OriginalTaskList.Length:F1}%)\n" +
                                 $"Team downloads errored: {faulted.Length}\n" +
                                 $"About {(elapsed * Math.Min(state.OriginalTaskList.Length / (1.0 * (completedCount + faulted.Length)), 100000000) - elapsed).ToLongString(showSeconds: false)} remaining" + faultedTeamsStatus.ToString()).ConfigureAwait(false);
            }