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); }
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)) { var temp = new CyberPatriot.Services.ScoreRetrieval.JsonScoreRetrievalService(); temp.Deserialize(await new StreamReader(unzipStream).ReadToEndAsync().ConfigureAwait(false)); existingArchive = temp.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); 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); }
public async Task Redownload(TeamId team) { var state = ScoreDownloader.GetState(); await state.ListLock.WaitAsync().ConfigureAwait(false); try { state.DetailsTaskList.Add(ScoreService.GetDetailsAsync(team)); } finally { state.ListLock.Release(); } await ReplyAsync($"Queued re-download for {team}.").ConfigureAwait(false); }