Exemple #1
0
        protected static ValueRange CreateUpdateCellsAlongRowRequest <T>(
            string sheetName, SpreadsheetColumn column, int rowNumber, IEnumerable <T> values)
        {
            Verify.IsNotNull(values, nameof(values));

            // Subtract one, since the start column already covers the first value
            SpreadsheetColumn endColumn = column + (values.Count() - 1);
            List <object>     innerList = new List <object>();

            foreach (T value in values)
            {
                innerList.Add(value);
            }

            ValueRange range = new ValueRange()
            {
                Range  = $"'{sheetName}'!{column}{rowNumber}:{endColumn}{rowNumber}",
                Values = new List <IList <object> >()
                {
                    innerList
                }
            };

            return(range);
        }
Exemple #2
0
        protected override IResult <List <ValueRange> > GetUpdateRangesForRoster(
            IReadOnlyDictionary <string, string> teamIdToNames, IEnumerable <IGrouping <string, PlayerTeamPair> > groupings)
        {
            Verify.IsNotNull(groupings, nameof(groupings));

            // Need to put teams in first row
            List <ValueRange>    ranges    = new List <ValueRange>();
            IEnumerable <string> teamNames = groupings
                                             .Select(grouping => teamIdToNames.TryGetValue(grouping.Key, out string name) ?
                                                     name :
                                                     grouping.First().PlayerDisplayName);

            ranges.Add(CreateUpdateCellsAlongRowRequest(RostersSheetName, RostersFirstTeamColumn, 1, teamNames));

            SpreadsheetColumn teamColumn = RostersFirstTeamColumn;

            foreach (IGrouping <string, PlayerTeamPair> grouping in groupings)
            {
                IEnumerable <string> playerNames = grouping.Select(pair => pair.PlayerDisplayName);
                ranges.Add(CreateUpdateCellsAlongColumnRequest(
                               RostersSheetName, teamColumn, RostersFirstPlayerRow, playerNames));

                teamColumn = teamColumn + 1;
            }

            return(new SuccessResult <List <ValueRange> >(ranges));
        }
Exemple #3
0
        protected override IResult <IEnumerable <ValueRange> > GetUpdateRangesForBonus(
            PhaseScore phaseScore, string[] teamIds, string sheetName, int row, int phasesCount)
        {
            Verify.IsNotNull(phaseScore, nameof(phaseScore));

            List <ValueRange> ranges = new List <ValueRange>();

            if (phaseScore.BonusScores?.Any() == true)
            {
                int bonusScoresIndex = Array.IndexOf(teamIds, phaseScore.BonusTeamId);
                if (bonusScoresIndex < 0)
                {
                    return(new FailureResult <IEnumerable <ValueRange> >(
                               $"Unknown bonus team in phase {row - this.FirstPhaseRow + 1}. Cannot accurately create a scoresheet."));
                }

                int bonusTotal = phaseScore.BonusScores.Sum();
                if (bonusTotal != 0 && bonusTotal != 10 && bonusTotal != 20 && bonusTotal != 30)
                {
                    return(new FailureResult <IEnumerable <ValueRange> >(
                               $"Invalid bonus value in phase {row - this.FirstPhaseRow + 1}. Value must be 0/10/20/30, but it was {bonusTotal}"));
                }

                SpreadsheetColumn bonusColumn = this.BonusColumns.Span[bonusScoresIndex];
                ranges.Add(CreateUpdateSingleCellRequest(sheetName, bonusColumn, row, bonusTotal));
            }
            else if (row != this.FirstPhaseRow + phasesCount - 1 || phaseScore.ScoringSplitsOnActions.Any())
            {
                // We need to find if anyone got it correct, and if so, what team they are on. Fill that bonus
                // column with 0.
                ScoringSplitOnScoreAction split = phaseScore.ScoringSplitsOnActions
                                                  .FirstOrDefault(split => split.Action.Score > 0);
                if (split != null)
                {
                    // TODO: See if there's a better way to get the individual's team (add it when we add the buzz?)
                    // Risk is if there's a team name that is the same as someone's ID
                    // If it's an individual who is a team, then the teamId will be null, but their user ID may be a
                    // team ID.
                    int bonusIndex = Array.IndexOf(
                        teamIds,
                        split.Action.Buzz.TeamId ?? split.Action.Buzz.UserId.ToString(CultureInfo.InvariantCulture));
                    if (bonusIndex < 0 || bonusIndex >= 2)
                    {
                        return(new FailureResult <IEnumerable <ValueRange> >(
                                   $"Unknown bonus team in phase {row - this.FirstPhaseRow + 1}. Cannot accurately create a scoresheet."));
                    }

                    ranges.Add(CreateUpdateSingleCellRequest(sheetName, this.BonusColumns.Span[bonusIndex], row, 0));
                }
            }

            return(new SuccessResult <IEnumerable <ValueRange> >(ranges));
        }
Exemple #4
0
        protected static ValueRange CreateUpdateSingleCellRequest <T>(
            string sheetName, SpreadsheetColumn column, int rowIndex, T value)
        {
            ValueRange range = new ValueRange()
            {
                Range  = $"'{sheetName}'!{column}{rowIndex}",
                Values = new List <IList <object> >()
                {
                    new List <object>()
                    {
                        value
                    }
                }
            };

            return(range);
        }
        protected override IResult <IEnumerable <ValueRange> > GetUpdateRangesForBonus(
            PhaseScore phaseScore, string[] teamIds, string sheetName, int row, int phasesCount)
        {
            Verify.IsNotNull(phaseScore, nameof(phaseScore));

            List <ValueRange> ranges = new List <ValueRange>();

            if (phaseScore.BonusScores?.Any() == true)
            {
                int bonusPartCount = phaseScore.BonusScores.Count();
                if (bonusPartCount != 3)
                {
                    return(new FailureResult <IEnumerable <ValueRange> >(
                               $"Non-three part bonus in phase {row - this.FirstPhaseRow + 1}. Number of parts: {bonusPartCount}. These aren't supported for the scoresheet."));
                }

                int bonusScoresIndex = Array.IndexOf(teamIds, phaseScore.BonusTeamId);
                if (bonusScoresIndex < 0)
                {
                    return(new FailureResult <IEnumerable <ValueRange> >(
                               $"Unknown bonus team in phase {row - this.FirstPhaseRow + 1}. Cannot accurately create a scoresheet."));
                }

                SpreadsheetColumn bonusColumn = this.BonusColumns.Span[bonusScoresIndex];
                ranges.Add(CreateUpdateCellsAlongRowRequest(
                               sheetName, bonusColumn, row, phaseScore.BonusScores.Select(score => score > 0).ToArray()));

                SpreadsheetColumn otherBonusColumn = this.BonusColumns.Span[this.BonusColumns.Length - bonusScoresIndex - 1];
                ranges.Add(CreateUpdateCellsAlongRowRequest(
                               sheetName, otherBonusColumn, row, ClearedBonusArray));
            }
            else
            {
                // Clear the bonus columns
                for (int i = 0; i < this.BonusColumns.Span.Length; i++)
                {
                    ranges.Add(CreateUpdateCellsAlongRowRequest(
                                   sheetName, this.BonusColumns.Span[i], row, ClearedBonusArray));
                }
            }

            return(new SuccessResult <IEnumerable <ValueRange> >(ranges));
        }
Exemple #6
0
        private IReadOnlyDictionary <ulong, SpreadsheetColumn> CreatePlayerIdToColumnMapping(
            IEnumerable <IGrouping <string, PlayerTeamPair> > playersByTeam)
        {
            Dictionary <ulong, SpreadsheetColumn> playerIdToColumn = new Dictionary <ulong, SpreadsheetColumn>();
            int startingColumnIndex = 0;

            foreach (IGrouping <string, PlayerTeamPair> grouping in playersByTeam)
            {
                SpreadsheetColumn startingColumn = this.StartingColumns.Span[startingColumnIndex];
                foreach (PlayerTeamPair pair in grouping)
                {
                    // TODO: If we ever support more than 6 players, we should bump up the next starting column
                    playerIdToColumn[pair.PlayerId] = startingColumn;
                    startingColumn = startingColumn + 1;
                }

                startingColumnIndex++;
            }

            return(playerIdToColumn);
        }
Exemple #7
0
        public async Task <IResult <string> > TryCreateScoresheet(GameState game, Uri sheetsUri, int roundNumber)
        {
            Verify.IsNotNull(game, nameof(game));
            Verify.IsNotNull(sheetsUri, nameof(sheetsUri));

            IReadOnlyDictionary <string, string> teamIdToNames = await game.TeamManager.GetTeamIdToNames();

            if (teamIdToNames.Count == 0 || teamIdToNames.Count > 2)
            {
                return(CreateFailureResult("Export only works if there are 1 or 2 teams in the game."));
            }

            // Make it an array so we don't keep re-evaluating the enumerable

            IReadOnlyDictionary <PlayerTeamPair, LastScoringSplit> players = await game.GetLastScoringSplits();

            IEnumerable <IGrouping <string, PlayerTeamPair> > playersByTeam = players.GroupBy(
                kvp => kvp.Key.TeamId, kvp => kvp.Key);

            if (playersByTeam.Any(grouping => grouping.Count() > this.PlayersPerTeamLimit))
            {
                return(CreateFailureResult("Export only currently works if there are at most 6 players on a team."));
            }

            string[] teamIds = playersByTeam.Select(grouping => grouping.Key).ToArray();

            IEnumerable <PhaseScore> phaseScores = await game.GetPhaseScores();

            int  phaseScoresCount = phaseScores.Count();
            bool trimScoresheet   = false;

            if (phaseScoresCount > this.PhasesLimit + 1 ||
                (phaseScoresCount == this.PhasesLimit + 1 && phaseScores.Last().ScoringSplitsOnActions.Any()))
            {
                trimScoresheet = true;
                phaseScores    = phaseScores.Take(this.PhasesLimit);
            }

            IReadOnlyDictionary <ulong, SpreadsheetColumn> playerIdToColumn = this.CreatePlayerIdToColumnMapping(playersByTeam);
            string            sheetName = this.GetSheetName(roundNumber);
            List <ValueRange> ranges    = new List <ValueRange>();

            // Write the names of the teams
            int startingColumnIndex = 0;

            foreach (string teamId in teamIds)
            {
                if (!teamIdToNames.TryGetValue(teamId, out string teamName))
                {
                    // We know the player exists since the teamIds came from the list of players
                    teamName = players.First(kvp => kvp.Key.TeamId == teamId).Key.PlayerDisplayName;
                }

                ranges.Add(CreateUpdateSingleCellRequest(
                               sheetName, this.StartingColumns.Span[startingColumnIndex], this.TeamNameRow, teamName));

                startingColumnIndex++;
            }

            foreach (PlayerTeamPair pair in players.Select(kvp => kvp.Key))
            {
                // TODO: Make this more efficient by putting all the player names in one update request
                SpreadsheetColumn column = playerIdToColumn[pair.PlayerId];
                ranges.Add(CreateUpdateSingleCellRequest(sheetName, column, this.PlayerNameRow, pair.PlayerDisplayName));
            }

            // Tossup scoring should be similar, but some bonuses are 3 parts, while others are 1 value
            // We can either let the sheets handle that, or do a switch here for 1 vs 3 part bonuses
            // but we have the weird DT thing with TJSheets, so let's have abstract classes deal with it
            int row         = this.FirstPhaseRow;
            int phasesCount = phaseScores.Count();

            foreach (PhaseScore phaseScore in phaseScores)
            {
                foreach (ScoringSplitOnScoreAction action in phaseScore.ScoringSplitsOnActions)
                {
                    if (!playerIdToColumn.TryGetValue(action.Action.Buzz.UserId, out SpreadsheetColumn column))
                    {
                        return(new FailureResult <string>(
                                   $"Unknown player {action.Action.Buzz.PlayerDisplayName} (ID {action.Action.Buzz.UserId}). Cannot accurately create a scoresheet. This happens in phase {row - this.FirstPhaseRow + 1}"));
                    }

                    ranges.Add(CreateUpdateSingleCellRequest(sheetName, column, row, action.Action.Score));
                }

                IResult <IEnumerable <ValueRange> > additionalRanges = this.GetAdditionalUpdateRangesForTossup(
                    phaseScore, teamIds, sheetName, row, phasesCount);
                if (!additionalRanges.Success)
                {
                    return(new FailureResult <string>(additionalRanges.ErrorMessage));
                }

                ranges.AddRange(additionalRanges.Value);

                // We need to move this to the UpdateRanges method, since we may need to write other stuff
                // Include that in the comment
                if (row <= this.LastBonusRow)
                {
                    IResult <IEnumerable <ValueRange> > bonusRanges = this.GetUpdateRangesForBonus(
                        phaseScore, teamIds, sheetName, row, phasesCount);
                    if (!bonusRanges.Success)
                    {
                        return(new FailureResult <string>(bonusRanges.ErrorMessage));
                    }

                    ranges.AddRange(bonusRanges.Value);
                }

                row++;
            }

            IResult <string> updateResult = await this.SheetsApi.UpdateGoogleSheet(
                ranges, this.GetClearRanges(sheetName), sheetsUri);

            if (!updateResult.Success)
            {
                return(updateResult);
            }

            string message = $"Game written to the scoresheet {sheetName}.";

            if (trimScoresheet)
            {
                message += $" This game had more tossups than space in the scoresheet, so only the first {this.PhasesLimit} tossup/bonus cycles were recorded.";
            }

            return(new SuccessResult <string>(message));
        }
Exemple #8
0
 public static SpreadsheetColumn Subtract(SpreadsheetColumn left, int right)
 {
     return(left - right);
 }
Exemple #9
0
 public static SpreadsheetColumn Add(SpreadsheetColumn left, int right)
 {
     return(left + right);
 }