public EmbedBuilder CreateTeamDetailsEmbed(ScoreboardDetails teamScore, TeamDetailRankingInformation rankingData = null)
        {
            if (teamScore == null)
            {
                throw new ArgumentNullException(nameof(teamScore));
            }

            var builder = new EmbedBuilder()
                          .WithTimestamp(teamScore.SnapshotTimestamp)
                          .WithTitle("Team " + teamScore.TeamId)
                          .WithDescription(Utilities.JoinNonNullNonEmpty(" | ", CompetitionLogic.GetEffectiveDivisionDescriptor(teamScore.Summary), teamScore.Summary.Tier, teamScore.Summary.Location))
                          .WithFooter(ScoreRetrieverMetadata.StaticSummaryLine);

            if (!string.IsNullOrWhiteSpace(teamScore.Comment))
            {
                builder.Description += "\n";
                builder.Description += teamScore.Comment;
            }

            // scoreboard link
            if (teamScore.OriginUri != null)
            {
                builder.Url = teamScore.OriginUri.ToString();
            }

            // location -> flag in thumbnail
            string flagUrl = FlagProvider.GetFlagUri(teamScore.Summary.Location);

            if (flagUrl != null)
            {
                builder.ThumbnailUrl = flagUrl;
            }

            // tier -> color on side
            // colors borrowed from AFA's spreadsheet
            if (teamScore.Summary.Tier.HasValue)
            {
                switch (teamScore.Summary.Tier.Value)
                {
                case Tier.Platinum:
                    // tweaked from AFA spreadsheet to be more distinct from silver
                    // AFA original is #DAE3F3
                    builder.WithColor(183, 201, 243);
                    break;

                case Tier.Gold:
                    builder.WithColor(0xFF, 0xE6, 0x99);
                    break;

                case Tier.Silver:
                    builder.WithColor(0xF2, 0xF2, 0xF2);
                    break;
                }
            }

            // TODO image lookup for location? e.g. thumbnail with flag?
            foreach (var item in teamScore.Images)
            {
                string       penaltyAppendage = item.Penalties != 0 ? " - " + Utilities.Pluralize("penalty", item.Penalties) : string.Empty;
                bool         overtime         = (item.Warnings & ScoreWarnings.TimeOver) == ScoreWarnings.TimeOver;
                bool         multiimage       = (item.Warnings & ScoreWarnings.MultiImage) == ScoreWarnings.MultiImage;
                string       warningAppendage = string.Empty;
                const string multiImageStr    = "**M**ulti-image";
                const string overTimeStr      = "**T**ime";
                if (overtime || multiimage)
                {
                    warningAppendage = "     Penalties: ";
                }
                if (overtime && multiimage)
                {
                    warningAppendage += multiImageStr + ", " + overTimeStr;
                }
                else if (overtime || multiimage)
                {
                    warningAppendage += multiimage ? multiImageStr : overTimeStr;
                }
                string vulnsString = ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.VulnerabilityDisplay, item.VulnerabilitiesFound, item.VulnerabilitiesRemaining + item.VulnerabilitiesFound) ? $" ({item.VulnerabilitiesFound}/{item.VulnerabilitiesFound + item.VulnerabilitiesRemaining} vulns{penaltyAppendage})" : string.Empty;
                string playTimeStr = ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.TimeDisplay, item.PlayTime) ? $" in {item.PlayTime:hh\\:mm}" : string.Empty;
                builder.AddField('`' + item.ImageName + $": {ScoreRetrieverMetadata.FormattingOptions.FormatScore(item.Score)}pts`", $"{ScoreRetrieverMetadata.FormattingOptions.FormatScore(item.Score)} points{vulnsString}{playTimeStr}{warningAppendage}");
            }

            string totalScoreTimeAppendage = string.Empty;

            if (ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.TimeDisplay, teamScore.Summary.PlayTime))
            {
                totalScoreTimeAppendage = $" in {teamScore.Summary.PlayTime:hh\\:mm}";
            }

            string totalPointsAppendage = string.Empty;

            if (teamScore.Images.All(i => i.PointsPossible != -1))
            {
                totalPointsAppendage =
                    "\n" + ScoreRetrieverMetadata.FormattingOptions.FormatScore(teamScore.Images.Sum(i => i.PointsPossible)) +
                    " points possible";
            }

            builder.AddInlineField("Total Score", $"{ScoreRetrieverMetadata.FormattingOptions.FormatScore(teamScore.Summary.TotalScore)} points" + totalScoreTimeAppendage + totalPointsAppendage);
            if (rankingData != null)
            {
                int myIndexInPeerList = rankingData.PeerIndex;

                double rawPercentile        = 1.0 - ((myIndexInPeerList + 1.0) / rankingData.PeerCount);
                int    multipliedPercentile = (int)Math.Round(rawPercentile * 1000);
                int    intPart   = multipliedPercentile / 10;
                int    floatPart = multipliedPercentile % 10;

                builder.AddInlineField("Rank", $"{Utilities.AppendOrdinalSuffix(myIndexInPeerList + 1)} place\n{(floatPart == 0 ? Utilities.AppendOrdinalSuffix(intPart) : $"{intPart}.{Utilities.AppendOrdinalSuffix(floatPart)}")} percentile");

                StringBuilder marginBuilder = new StringBuilder();
                if (myIndexInPeerList > 0)
                {
                    int marginUnderFirst = rankingData.Peers[0].TotalScore - teamScore.Summary.TotalScore;
                    marginBuilder.AppendLine($"{ScoreRetrieverMetadata.FormattingOptions.FormatLabeledScoreDifference(marginUnderFirst)} under 1st place");
                }
                if (myIndexInPeerList >= 2)
                {
                    int marginUnderAbove = rankingData.Peers[myIndexInPeerList - 1].TotalScore - teamScore.Summary.TotalScore;
                    marginBuilder.AppendLine($"{ScoreRetrieverMetadata.FormattingOptions.FormatLabeledScoreDifference(marginUnderAbove)} under {Utilities.AppendOrdinalSuffix(myIndexInPeerList)} place");
                }
                if (myIndexInPeerList < rankingData.PeerCount - 1)
                {
                    int marginAboveUnder = teamScore.Summary.TotalScore - rankingData.Peers[myIndexInPeerList + 1].TotalScore;
                    marginBuilder.AppendLine($"{ScoreRetrieverMetadata.FormattingOptions.FormatLabeledScoreDifference(marginAboveUnder)} above {Utilities.AppendOrdinalSuffix(myIndexInPeerList + 2)} place");
                }

                // TODO division- and round-specific margins
                builder.AddInlineField("Margin", marginBuilder.ToString());

                StringBuilder standingFieldBuilder = new StringBuilder();
                standingFieldBuilder.AppendLine(Utilities.AppendOrdinalSuffix(myIndexInPeerList + 1) + " of " + Utilities.Pluralize("peer team", rankingData.PeerCount));

                // non-peer rankings use parentheticals - peer rankings are used for the rest of the logic
                // if peer teams != div+tier teams
                if (rankingData.PeerCount != rankingData.TierCount)
                {
                    // tier ranking, differing from peer ranking
                    standingFieldBuilder.AppendLine(Utilities.AppendOrdinalSuffix(rankingData.TierIndex + 1) + " of " + Utilities.Pluralize("team", rankingData.TierCount) + " in tier");
                }
                if (rankingData.PeerCount != rankingData.DivisionCount)
                {
                    // division ranking, differing from peer ranking
                    standingFieldBuilder.AppendLine(Utilities.AppendOrdinalSuffix(rankingData.DivisionIndex + 1) + " of " + Utilities.Pluralize("team", rankingData.DivisionCount) + " in division");
                }
                builder.AddInlineField("Standing", standingFieldBuilder.ToString());
            }
Exemplo n.º 2
0
        public EmbedBuilder CreateTeamDetailsEmbed(ScoreboardDetails teamScore, CompleteScoreboardSummary completeScoreboard = null, ScoreboardFilterInfo peerFilter = default(ScoreboardFilterInfo), TimeZoneInfo timeZone = null)
        {
            if (teamScore == null)
            {
                throw new ArgumentNullException(nameof(teamScore));
            }

            var builder = new EmbedBuilder()
                          .WithTimestamp(teamScore.SnapshotTimestamp)
                          .WithTitle("Team " + teamScore.TeamId)
                          .WithDescription(Utilities.JoinNonNullNonEmpty(" | ", CompetitionLogic.GetEffectiveDivisionDescriptor(teamScore.Summary), teamScore.Summary.Tier, LocationResolution.GetFullName(teamScore.Summary.Location)))
                          .WithFooter(ScoreRetrieverMetadata.StaticSummaryLine);

            if (!string.IsNullOrWhiteSpace(teamScore.Comment))
            {
                builder.Description += "\n";
                builder.Description += teamScore.Comment;
            }

            // scoreboard link
            if (teamScore.OriginUri != null)
            {
                builder.Url = teamScore.OriginUri.ToString();
            }

            // location -> flag in thumbnail
            Uri flagUrl = LocationResolution?.GetFlagUriOrNull(teamScore.Summary.Location);

            if (flagUrl != null)
            {
                builder.ThumbnailUrl = flagUrl.ToString();
            }

            // tier -> color on side
            // colors borrowed from AFA's spreadsheet
            if (teamScore.Summary.Tier.HasValue)
            {
                switch (teamScore.Summary.Tier.Value)
                {
                case Tier.Platinum:
                    // tweaked from AFA spreadsheet to be more distinct from silver
                    // AFA original is #DAE3F3
                    builder.WithColor(183, 201, 243);
                    break;

                case Tier.Gold:
                    builder.WithColor(0xFF, 0xE6, 0x99);
                    break;

                case Tier.Silver:
                    // tweaked from AFA spreadsheet to be more distinct from platinum, and to look less white
                    // AFA original is #F2F2F2
                    builder.WithColor(0x90, 0x90, 0x90);
                    break;
                }
            }

            // TODO image lookup for location? e.g. thumbnail with flag?
            foreach (var item in teamScore.Images)
            {
                string       penaltyAppendage = item.Penalties != 0 ? " - " + Utilities.Pluralize("penalty", item.Penalties) : string.Empty;
                bool         overtime         = (item.Warnings & ScoreWarnings.TimeOver) == ScoreWarnings.TimeOver;
                bool         multiimage       = (item.Warnings & ScoreWarnings.MultiImage) == ScoreWarnings.MultiImage;
                string       warningAppendage = string.Empty;
                const string multiImageStr    = "**M**ulti-instance";
                const string overTimeStr      = "**T**ime";
                if (overtime || multiimage)
                {
                    warningAppendage = "\nWarnings: ";
                }
                if (overtime && multiimage)
                {
                    warningAppendage += multiImageStr + ", " + overTimeStr;
                }
                else if (overtime || multiimage)
                {
                    warningAppendage += multiimage ? multiImageStr : overTimeStr;
                }
                string vulnsString = ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.VulnerabilityDisplay, item.VulnerabilitiesFound, item.VulnerabilitiesRemaining + item.VulnerabilitiesFound) ? $" ({item.VulnerabilitiesFound}/{item.VulnerabilitiesFound + item.VulnerabilitiesRemaining} vulns{penaltyAppendage})" : string.Empty;
                string playTimeStr = ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.TimeDisplay, item.PlayTime) ? $" in {item.PlayTime.ToHoursMinutesSecondsString()}" : string.Empty;
                builder.AddField('`' + item.ImageName + $": {ScoreRetrieverMetadata.FormattingOptions.FormatScore(item.Score)}pts`", $"{ScoreRetrieverMetadata.FormattingOptions.FormatScore(item.Score)} points{vulnsString}{playTimeStr}{warningAppendage}");
            }

            string totalScoreTimeAppendage = string.Empty;

            if (ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.TimeDisplay, teamScore.Summary.PlayTime))
            {
                totalScoreTimeAppendage = $" in {teamScore.Summary.PlayTime.ToHoursMinutesSecondsString()}";
            }

            string totalPointsAppendage = string.Empty;

            if (teamScore.Images.All(i => i.PointsPossible != -1))
            {
                totalPointsAppendage =
                    "\n" + ScoreRetrieverMetadata.FormattingOptions.FormatScore(teamScore.Images.Sum(i => i.PointsPossible)) +
                    " points possible";
            }

            builder.AddInlineField("Total Score", $"{ScoreRetrieverMetadata.FormattingOptions.FormatScore(teamScore.Summary.TotalScore)} points" + totalScoreTimeAppendage + totalPointsAppendage);

            if (teamScore.Summary.Warnings != 0)
            {
                string warningsOverview = null;
                if ((teamScore.Summary.Warnings & ScoreWarnings.MultiImage) == ScoreWarnings.MultiImage)
                {
                    warningsOverview = "Multiple Instances";
                }

                if ((teamScore.Summary.Warnings & ScoreWarnings.TimeOver) == ScoreWarnings.TimeOver)
                {
                    if (warningsOverview == null)
                    {
                        warningsOverview = "";
                    }
                    else
                    {
                        warningsOverview += "\n";
                    }

                    warningsOverview += "Time Limit Exceeded";
                }

                builder.AddInlineField("Warnings", warningsOverview);
            }

            var timingFieldBuilder = new StringBuilder();

            if (ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.TimeDisplay, teamScore.ScoreTime))
            {
                if (timingFieldBuilder.Length > 0)
                {
                    timingFieldBuilder.AppendLine();
                }
                timingFieldBuilder.AppendFormat("Score achieved in {0}", teamScore.ScoreTime.ToHoursMinutesSecondsString());
            }

            DateTimeOffset?maxImageTime = null;

            if (teamScore.ImageScoresOverTime != null)
            {
                foreach (var dto in teamScore.ImageScoresOverTime.Select(x => x.Value.Keys.Last()))
                {
                    if (!maxImageTime.HasValue || dto > maxImageTime.Value)
                    {
                        maxImageTime = dto;
                    }
                }
            }

            if (maxImageTime.HasValue)
            {
                if (timingFieldBuilder.Length > 0)
                {
                    timingFieldBuilder.AppendLine();
                }
                timingFieldBuilder.Append("Score last updated:");
                timingFieldBuilder.AppendLine().Append("\u00A0\u00A0"); //NBSP x2

                if (DateTimeOffset.UtcNow - maxImageTime.Value < TimeSpan.FromDays(1))
                {
                    timingFieldBuilder.Append((DateTimeOffset.UtcNow - maxImageTime.Value).ToLongString(showSeconds: false)).Append(" ago");
                }
                else
                {
                    DateTimeOffset timestamp = timeZone == null ? maxImageTime.Value : TimeZoneInfo.ConvertTime(maxImageTime.Value, timeZone);
                    timingFieldBuilder.AppendFormat("{0:g} ", timestamp);
                    timingFieldBuilder.Append(TimeZoneNames.TZNames.GetAbbreviationsForTimeZone(timeZone.Id, "en-US").Generic.Replace("UTC Time", "UTC"));
                }
            }

            if (timingFieldBuilder.Length > 0)
            {
                builder.AddInlineField("Timing", timingFieldBuilder.ToString());
            }

            if (completeScoreboard != null)
            {
                var peerList          = completeScoreboard.Clone().WithFilter(peerFilter).TeamList;
                int myIndexInPeerList = peerList.IndexOfWhere(x => x.TeamId == teamScore.TeamId);

                double rawPercentile        = 1.0 - ((myIndexInPeerList + 1.0) / peerList.Count);
                int    multipliedPercentile = (int)Math.Round(rawPercentile * 1000);
                int    intPart   = multipliedPercentile / 10;
                int    floatPart = multipliedPercentile % 10;

                builder.AddInlineField("Rank", $"{Utilities.AppendOrdinalSuffix(myIndexInPeerList + 1)} place\n{(floatPart == 0 ? Utilities.AppendOrdinalSuffix(intPart) : $"{intPart}.{Utilities.AppendOrdinalSuffix(floatPart)}")} percentile");

                StringBuilder marginBuilder = new StringBuilder();
                if (myIndexInPeerList > 0)
                {
                    double marginUnderFirst = peerList[0].TotalScore - teamScore.Summary.TotalScore;
                    marginBuilder.AppendLine($"{ScoreRetrieverMetadata.FormattingOptions.FormatLabeledScoreDifference(marginUnderFirst)} under 1st place");
                }
                if (myIndexInPeerList >= 2)
                {
                    double marginUnderAbove = peerList[myIndexInPeerList - 1].TotalScore - teamScore.Summary.TotalScore;
                    marginBuilder.AppendLine($"{ScoreRetrieverMetadata.FormattingOptions.FormatLabeledScoreDifference(marginUnderAbove)} under {Utilities.AppendOrdinalSuffix(myIndexInPeerList)} place");
                }
                if (myIndexInPeerList < peerList.Count - 1)
                {
                    double marginAboveUnder = teamScore.Summary.TotalScore - peerList[myIndexInPeerList + 1].TotalScore;
                    marginBuilder.AppendLine($"{ScoreRetrieverMetadata.FormattingOptions.FormatLabeledScoreDifference(marginAboveUnder)} above {Utilities.AppendOrdinalSuffix(myIndexInPeerList + 2)} place");
                }

                // TODO division- and round-specific margins
                builder.AddInlineField("Margin", marginBuilder.ToString());

                StringBuilder standingFieldBuilder = new StringBuilder();

                IList <ScoreboardSummaryEntry> subPeer = null;
                string subPeerLabel = null;

                if (!peerFilter.Category.HasValue && teamScore.Summary.Category.HasValue)
                {
                    var myCategory = teamScore.Summary.Category.Value;
                    subPeer      = peerList.Where(x => x.Category == myCategory).ToIList();
                    subPeerLabel = " in category";
                }
                else if (peerFilter.Location == null && teamScore.Summary.Location != null)
                {
                    var myLocation = teamScore.Summary.Location;
                    subPeer      = peerList.Where(x => x.Location == myLocation).ToIList();
                    subPeerLabel = " in state";
                }

                if (subPeerLabel != null)
                {
                    standingFieldBuilder.AppendLine(Utilities.AppendOrdinalSuffix(subPeer.IndexOfWhere(x => x.TeamId == teamScore.TeamId) + 1) + " of " + Utilities.Pluralize("peer team", subPeer.Count) + subPeerLabel);
                }

                standingFieldBuilder.AppendLine(Utilities.AppendOrdinalSuffix(myIndexInPeerList + 1) + " of " + Utilities.Pluralize("peer team", peerList.Count));

                // if peer teams != div+tier teams
                if ((peerFilter.Category.HasValue || peerFilter.Location != null) && peerFilter.Tier.HasValue)
                {
                    // tier ranking, differing from peer ranking
                    var tierTeams = completeScoreboard.Clone().WithFilter(new ScoreboardFilterInfo(peerFilter.Division, peerFilter.Tier)).TeamList;
                    standingFieldBuilder.AppendLine(Utilities.AppendOrdinalSuffix(tierTeams.IndexOfWhere(x => x.TeamId == teamScore.TeamId) + 1) + " of " + Utilities.Pluralize("team", tierTeams.Count) + " in tier");
                }
                if (peerFilter.Category.HasValue || peerFilter.Location != null || peerFilter.Tier.HasValue)
                {
                    // division ranking, differing from peer ranking
                    var divTeams = completeScoreboard.Clone().WithFilter(new ScoreboardFilterInfo(peerFilter.Division, null)).TeamList;
                    standingFieldBuilder.AppendLine(Utilities.AppendOrdinalSuffix(divTeams.IndexOfWhere(x => x.TeamId == teamScore.TeamId) + 1) + " of " + Utilities.Pluralize("team", divTeams.Count) + " in division");
                }
                builder.AddInlineField("Standing", standingFieldBuilder.ToString());
            }