public override Task <TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services) { TeamId result; if (TeamId.TryParse(input, out result)) { return(Task.FromResult(TypeReaderResult.FromSuccess(result))); } else if (input != null && input.Length == 4 && int.TryParse(input, out int teamNumber) && teamNumber >= 0) { return(Task.FromResult(TypeReaderResult.FromSuccess(new TeamId(TeamIdTypeConverter.DefaultCompetition, teamNumber)))); } return(Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input could not be parsed as a team ID."))); }
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { // assumes we're performing a supported conversion: string parse or team # => TeamId; or teamId => string if (value == null) { throw new ArgumentNullException(nameof(value)); } switch (value) { case string s: if (TeamId.TryParse(s, out TeamId val)) { return(val); } else if (s.Length == DefaultCompetitionNumericIdLength && int.TryParse(s, out int teamNumber)) { // ctor does bounds checking return(new TeamId(DefaultCompetition, teamNumber)); } break; case short tempShort: // ctor does bounds checking return(new TeamId(DefaultCompetition, tempShort)); case int i: // ctor does bounds checking return(new TeamId(DefaultCompetition, i)); case TeamId t: return(t.ToString()); } throw new NotSupportedException("The specified conversion is not supported."); }
static void Main(string[] args) { string jsonPath; if (args.Length < 1) { Console.Error.WriteLine("Enter path to JSON file:"); jsonPath = Console.ReadLine(); } else { jsonPath = args[0]; } Console.Error.WriteLine($"Got {jsonPath} as JSON path"); string categoryPath; if (args.Length < 2) { Console.Error.WriteLine("Enter path to category map file:"); categoryPath = Console.ReadLine(); } else { categoryPath = args[1]; } Console.Error.WriteLine($"Got {categoryPath} as category mapfile path"); var input = JsonConvert.DeserializeObject <Output>(File.ReadAllText(jsonPath)); var teamCategoryDictionary = File.ReadAllLines(categoryPath).Select(l => l.Trim().Split(new[] { ':' }, 2)).Where(l => TeamId.TryParse(l[0], out TeamId _)).ToDictionary(l => TeamId.Parse(l[0]), l => ServiceCategoryExtensions.ParseCanonicalName(l[1])); // fix summary data foreach (var teamSummary in input.summary.TeamList) { if (teamSummary.Category == null && teamCategoryDictionary.TryGetValue(teamSummary.TeamId, out ServiceCategory newCategory)) { teamSummary.Category = newCategory; } } // fix details foreach (var knownCategory in teamCategoryDictionary) { if (input.teams.TryGetValue(knownCategory.Key, out var teamDetails) && teamDetails.Summary.Category == null) { teamDetails.Summary.Category = knownCategory.Value; } } Console.Write(JsonConvert.SerializeObject(input)); }
static void Main(string[] args) { Console.WriteLine("Enter path to CSV file:"); string path = Console.ReadLine(); Console.WriteLine("Enter timestamp:"); string timestamp = Console.ReadLine(); Console.WriteLine("Enter path to all service category map file (or empty string):"); string servicePath = Console.ReadLine(); Console.WriteLine("Enter round number:"); int roundNumber = int.Parse(Console.ReadLine()); Console.WriteLine("Enter origin URI:"); string originUri = Console.ReadLine(); Dictionary <TeamId, string> categories = new Dictionary <TeamId, string>(); if (servicePath != "") { categories = File.ReadAllLines(servicePath).Select(x => x.Split(':')).Where(x => TeamId.TryParse(x[0], out TeamId _)).ToDictionary(x => TeamId.Parse(x[0]), x => x[1]); } var lines = File.ReadAllLines(path); CompleteScoreboardSummary summary = new CompleteScoreboardSummary(); summary.TeamList = new List <ScoreboardSummaryEntry>(); summary.SnapshotTimestamp = DateTimeOffset.Parse(timestamp); summary.OriginUri = string.IsNullOrEmpty(originUri) ? null : new Uri(originUri); Console.WriteLine("Loading score data"); foreach (string[] data in lines.Skip(1).Select(line => line.Split(','))) { ScoreboardSummaryEntry entry = new ScoreboardSummaryEntry { TeamId = TeamId.Parse(data[0]), Division = Enum.Parse <Division>(data[1].Replace(" ", ""), true), Category = string.IsNullOrEmpty(data[2]) ? categories.TryGetValue(TeamId.Parse(data[0]), out string c) ? c : null : data[2], Location = data[3], Tier = Enum.TryParse <Tier>(data[4], true, out Tier t) ? t : (Tier?)null, ImageCount = int.Parse(data[5]), PlayTime = ParseTimeSpan(data[6]), TotalScore = int.Parse(data[7]), Warnings = (data[8].Contains("M") ? ScoreWarnings.MultiImage : 0) | (data[8].Contains("T") ? ScoreWarnings.TimeOver : 0) }; summary.TeamList.Add(entry); } Console.WriteLine("Generating output data"); var o = new Output { round = roundNumber, summary = summary, teams = summary.TeamList.Select(x => new ScoreboardDetails { Images = new List <ScoreboardImageDetails> { new ScoreboardImageDetails { ImageName = "All Points", Penalties = 0, PlayTime = x.PlayTime, PointsPossible = x.ImageCount * 100, Score = x.TotalScore, VulnerabilitiesFound = 0, VulnerabilitiesRemaining = 0, Warnings = x.Warnings } }, ImageScoresOverTime = null, OriginUri = null, ScoreTime = x.PlayTime, SnapshotTimestamp = DateTimeOffset.Parse(timestamp), Summary = x }).ToDictionary(x => x.TeamId, x => x) }; File.WriteAllText("scores.json", JsonConvert.SerializeObject(o)); Console.WriteLine("Done"); Console.ReadKey(); }
public async Task <ScoreboardDetails> GetDetailsAsync(TeamId team) { if (team == null) { throw new ArgumentNullException(nameof(team)); } string detailsPage; Uri detailsUri = BuildDetailsUri(team); await RateLimiter.GetWorkAuthorizationAsync().ConfigureAwait(false); Task <string> stringTask = Client.GetStringAsync(detailsUri); RateLimiter.AddPrerequisite(stringTask); try { detailsPage = await stringTask.ConfigureAwait(false); // hacky, cause they don't return a proper error page for nonexistant teams if (!detailsPage.Contains(@"<div id='chart_div' class='chart'>")) { throw new ArgumentException("The given team does not exist."); } } catch (HttpRequestException e) { throw new InvalidOperationException("Error getting team details page, perhaps the scoreboard is offline?", e); } ScoreboardDetails retVal = new ScoreboardDetails(); retVal.OriginUri = detailsUri; HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(detailsPage); var timestampHeader = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/h2[2]")?.InnerText; retVal.SnapshotTimestamp = timestampHeader == null ? DateTimeOffset.UtcNow : DateTimeOffset.Parse(timestampHeader.Replace("Generated At: ", string.Empty).Replace("UTC", "+0:00")); var summaryHeaderRow = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/table[1]/tr[1]"); var summaryHeaderRowData = summaryHeaderRow.ChildNodes.Select(x => x.InnerText).ToArray(); var summaryRow = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/table[1]/tr[2]"); var summaryRowData = summaryRow.ChildNodes.Select(x => x.InnerText).ToArray(); ParseDetailedSummaryEntry(retVal, summaryRowData); // summary parsed var imagesTable = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/table[2]").ChildNodes.Where(n => n.Name != "#text").ToArray(); for (int i = 1; i < imagesTable.Length; i++) { // skip team IDs to account for legacy scoreboards string[] dataEntries = imagesTable[i].ChildNodes.Select(n => n.InnerText.Trim()).SkipWhile(s => TeamId.TryParse(s, out TeamId _)).ToArray(); ScoreboardImageDetails image = new ScoreboardImageDetails(); image.PointsPossible = 100; image.ImageName = dataEntries[0]; image.PlayTime = Utilities.ParseHourMinuteSecondTimespan(dataEntries[1]); image.VulnerabilitiesFound = int.Parse(dataEntries[2]); image.VulnerabilitiesRemaining = int.Parse(dataEntries[3]); image.Penalties = int.Parse(dataEntries[4]); image.Score = double.Parse(dataEntries[5]); image.Warnings |= dataEntries[6].Contains("T") ? ScoreWarnings.TimeOver : 0; image.Warnings |= dataEntries[6].Contains("M") ? ScoreWarnings.MultiImage : 0; retVal.Images.Add(image); } // reparse summary table (CCS+Cisco case) // pseudoimages: Cisco, administrative adjustment (usually penalty) int ciscoIndex = summaryHeaderRowData.IndexOfWhere(x => x.ToLower().Contains("cisco")); int penaltyIndex = summaryHeaderRowData.IndexOfWhere(x => x.ToLower().Contains("adjust")); ScoreboardImageDetails CreatePseudoImage(string name, double score, double possible) { var image = new ScoreboardImageDetails(); image.PointsPossible = possible; image.ImageName = name; image.Score = score; image.VulnerabilitiesFound = 0; image.VulnerabilitiesRemaining = 0; image.Penalties = 0; image.Warnings = 0; image.PlayTime = TimeSpan.Zero; return(image); } if (ciscoIndex != -1) { // pseudoimage // FIXME shouldn't display vulns and penalties and time double ciscoDenom = -1; try { ciscoDenom = _roundInferenceService.GetCiscoPointsPossible(Round, retVal.Summary.Division, retVal.Summary.Tier); } catch { // probably because round 0; unknown total } retVal.Images.Add(CreatePseudoImage("Cisco (Total)", double.Parse(summaryRowData[ciscoIndex]), ciscoDenom)); } if (penaltyIndex != -1) { retVal.Images.Add(CreatePseudoImage("Administrative Adjustment", double.Parse(summaryRowData[penaltyIndex]), 0)); } // score graph try { var teamScoreGraphHeader = new Regex(@"\['Time'(?:, '(\w+)')* *\]"); var teamScoreGraphEntry = new Regex(@"\['(\d{2}/\d{2} \d{2}:\d{2})'(?:, (-?\d+|null))*\]"); Match headerMatch = teamScoreGraphHeader.Match(detailsPage); if (headerMatch?.Success ?? false) { retVal.ImageScoresOverTime = new Dictionary <string, SortedDictionary <DateTimeOffset, int?> >(); string[] imageHeaders = headerMatch.Groups[1].Captures.Cast <Capture>().Select(c => c.Value).ToArray(); SortedDictionary <DateTimeOffset, int?>[] dictArr = new SortedDictionary <DateTimeOffset, int?> [imageHeaders.Length]; for (int i = 0; i < dictArr.Length; i++) { dictArr[i] = new SortedDictionary <DateTimeOffset, int?>(); retVal.ImageScoresOverTime[imageHeaders[i]] = dictArr[i]; } foreach (var m in teamScoreGraphEntry.Matches(detailsPage).Cast <Match>().Where(g => g?.Success ?? false)) { DateTimeOffset dto = default(DateTimeOffset); try { // MM/dd hh:mm string dateStr = m.Groups[1].Value; string[] dateStrComponents = dateStr.Split(' '); string[] dateComponents = dateStrComponents[0].Split('/'); string[] timeComponents = dateStrComponents[1].Split(':'); dto = new DateTimeOffset(DateTimeOffset.UtcNow.Year, int.Parse(dateComponents[0]), int.Parse(dateComponents[1]), int.Parse(timeComponents[0]), int.Parse(timeComponents[1]), 0, TimeSpan.Zero); } catch { continue; } var captures = m.Groups[2].Captures; for (int i = 0; i < captures.Count; i++) { int?scoreVal = null; if (int.TryParse(captures[i].Value, out int thingValTemp)) { scoreVal = thingValTemp; } dictArr[i][dto] = scoreVal; } } } } catch { // TODO log } return(retVal); }