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.");
        }
示例#3
0
        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);
        }