public async Task <IActionResult> OnGetAsync()
        {
            List <Puzzle> puzzles = await PuzzleHelper.GetPuzzles(_context, Event, LoggedInUser, EventRole);

            Dictionary <int, List <string> > puzzleAuthors = await(from author in _context.PuzzleAuthors
                                                                   where author.Puzzle.Event == Event
                                                                   group author by author.PuzzleID into authorList
                                                                   select new { Puzzle = authorList.Key, Authors = (from a in authorList select a.Author.Name).ToList() }).ToDictionaryAsync(x => x.Puzzle, x => x.Authors);
            Dictionary <int, ContentFile> puzzleFiles = await(from file in _context.ContentFiles
                                                              where file.Event == Event && file.FileType == ContentFileType.Puzzle
                                                              select file).ToDictionaryAsync(file => file.PuzzleID);
            Dictionary <int, ContentFile> puzzleAnswers = await(from file in _context.ContentFiles
                                                                where file.Event == Event && file.FileType == ContentFileType.Answer
                                                                select file).ToDictionaryAsync(file => file.PuzzleID);
            Dictionary <int, List <string> > puzzlePrereqs = await(from prerequisite in _context.Prerequisites
                                                                   where prerequisite.Puzzle.Event == Event
                                                                   group prerequisite by prerequisite.PuzzleID into prereqs
                                                                   select new { Puzzle = prereqs.Key, Prereqs = (from p in prereqs orderby p.Prerequisite.Name select p.Prerequisite.Name).ToList() }).ToDictionaryAsync(x => x.Puzzle, x => x.Prereqs);
            Dictionary <int, ResponseData> puzzleResponses = await(from response in _context.Responses
                                                                   where response.Puzzle.Event == Event
                                                                   group response by response.PuzzleID into responseList
                                                                   select new { Puzzle = responseList.Key, Responses = new ResponseData {
                                                                                    ResponseCount = responseList.Count(), HasAnswer = (from r in responseList where r.IsSolution select r).Count() > 0
                                                                                } }).ToDictionaryAsync(x => x.Puzzle, x => x.Responses);
            Dictionary <int, IEnumerable <int> > puzzleHints = await(from hint in _context.Hints
                                                                     where hint.Puzzle.Event == Event
                                                                     group hint by hint.Puzzle.ID into hints
                                                                     select new { Puzzle = hints.Key, Hints = (from h in hints select h.Cost) }).ToDictionaryAsync(x => x.Puzzle, x => x.Hints);

            PuzzleData = new List <PuzzleView>();
            foreach (Puzzle puzzle in puzzles)
            {
                puzzleAuthors.TryGetValue(puzzle.ID, out List <string> authors);
                puzzleFiles.TryGetValue(puzzle.ID, out ContentFile puzzleFile);
                puzzleAnswers.TryGetValue(puzzle.ID, out ContentFile puzzleAnswer);
                puzzlePrereqs.TryGetValue(puzzle.ID, out List <string> prereqs);
                puzzleResponses.TryGetValue(puzzle.ID, out ResponseData responses);
                puzzleHints.TryGetValue(puzzle.ID, out IEnumerable <int> hints);

                int totalHintCostThisPuzzle = 0;
                int hintsCountThisPuzzle    = 0;
                if (hints != null)
                {
                    int totalDiscount = 0;

                    hintsCountThisPuzzle = hints.Count();
                    foreach (int cost in hints)
                    {
                        totalDiscount = Math.Min(totalDiscount, cost);
                    }

                    // totalDiscount is 0 or negative. Start with that cost (flipped to positive
                    // as it must be paid in order to reduce the cost of the others.
                    totalHintCostThisPuzzle = -totalDiscount;

                    foreach (int cost in hints)
                    {
                        // Negative cost hints will be ignored because Max(0,negative) is 0.
                        // Positive cost hints will only be counted for the cost above the discount.
                        // (The discount is counted against each other hint.)
                        totalHintCostThisPuzzle += Math.Max(0, cost + totalDiscount);
                    }
                }

                PuzzleData.Add(new PuzzleView()
                {
                    Puzzle             = puzzle,
                    Authors            = authors != null ? string.Join(", ", authors) : "",
                    PuzzleFile         = puzzleFile,
                    AnswerFile         = puzzleAnswer,
                    Prerequisites      = prereqs != null ? string.Join(", ", prereqs) : "",
                    PrerequisitesCount = prereqs != null ? prereqs.Count() : 0,
                    Responses          = responses != null ? responses : new ResponseData {
                        ResponseCount = 0, HasAnswer = false
                    },
                    Hints         = hintsCountThisPuzzle,
                    TotalHintCost = totalHintCostThisPuzzle
                });

                TotalHints    += hintsCountThisPuzzle;
                TotalHintCost += totalHintCostThisPuzzle;
            }

            return(Page());
        }
Esempio n. 2
0
        public async Task <IActionResult> OnPostUnlock(int puzzleId, int unlockId)
        {
            Puzzle puzzle = await(from puzz in _context.Puzzles
                                  where puzz.ID == puzzleId
                                  select puzz).FirstOrDefaultAsync();

            if (puzzle == null)
            {
                return(NotFound());
            }

            // Restrict this page to whistle stop non-puzzles
            if (puzzle.MinutesOfEventLockout == 0 || puzzle.IsPuzzle)
            {
                return(NotFound());
            }

            Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser);

            using (var transaction = _context.Database.BeginTransaction())
            {
                var puzzleStateQuery           = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team);
                PuzzleStatePerTeam prereqState = await(from pspt in puzzleStateQuery
                                                       where pspt.PuzzleID == puzzle.ID
                                                       select pspt).FirstOrDefaultAsync();
                if (prereqState == null)
                {
                    return(NotFound());
                }

                // Only move forward if the prereq is open and unsolved
                if (prereqState.UnlockedTime == null || prereqState.SolvedTime != null)
                {
                    return(NotFound("Your team has already chosen and can't choose again"));
                }

                PuzzleStatePerTeam unlockState = await(from pspt in puzzleStateQuery
                                                       where pspt.PuzzleID == unlockId
                                                       select pspt).FirstOrDefaultAsync();
                if (unlockState == null)
                {
                    return(NotFound());
                }

                // The chosen puzzle must be locked (and unsolved)
                if (unlockState.UnlockedTime != null || unlockState.SolvedTime != null)
                {
                    return(NotFound("You've already chosen this puzzle"));
                }

                // Ensure the puzzle is actually one of the unlock options
                Prerequisites prereq = await(from pre in _context.Prerequisites
                                             where pre.PrerequisiteID == puzzleId && pre.PuzzleID == unlockId
                                             select pre).FirstOrDefaultAsync();
                if (prereq == null)
                {
                    return(NotFound());
                }

                await PuzzleStateHelper.SetSolveStateAsync(_context, Event, puzzle, team, DateTime.UtcNow);

                await PuzzleStateHelper.SetUnlockStateAsync(_context, Event, unlockState.Puzzle, team, DateTime.UtcNow);

                transaction.Commit();
            }

            Puzzle puzzleToUnlock = await(from p in _context.Puzzles
                                          where p.ID == unlockId
                                          select p).FirstOrDefaultAsync();

            string puzzleUrl;

            if (puzzleToUnlock.CustomURL != null)
            {
                puzzleUrl = PuzzleHelper.GetFormattedUrl(puzzleToUnlock, Event.ID);
            }
            else
            {
                puzzleUrl = puzzleToUnlock.PuzzleFile.UrlString;
            }

            return(Redirect(puzzleUrl));
        }
        public async Task <IActionResult> OnPostImportAsync()
        {
            // the BindProperty only binds the event ID, let's get the rest
            if (await _context.Events.Where((e) => e.ID == ImportEventID).FirstOrDefaultAsync() == null)
            {
                return(NotFound());
            }

            // verify that we're an admin of the import event. current event administratorship is already validated.
            if (!await _context.EventAdmins.Where(ea => ea.Event.ID == ImportEventID && ea.Admin == LoggedInUser).AnyAsync())
            {
                return(Forbid());
            }

            var sourceEventAuthors = await _context.EventAuthors.Where((a) => a.Event.ID == ImportEventID).ToListAsync();

            var sourcePuzzles = await _context.Puzzles.Where((p) => p.Event.ID == ImportEventID).ToListAsync();

            // TODO: replace this with a checkbox and sufficient danger warnings about duplicate titles
            bool deletePuzzleIfPresent = true;

            using (var transaction = _context.Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
            {
                // Step 1: Make sure all authors exist
                foreach (var sourceEventAuthor in sourceEventAuthors)
                {
                    var destEventAuthor = await _context.EventAuthors.Where((e) => e.Event == Event && e.Author == sourceEventAuthor.Author).FirstOrDefaultAsync();

                    if (destEventAuthor == null)
                    {
                        destEventAuthor       = new EventAuthors(sourceEventAuthor);
                        destEventAuthor.Event = Event;
                        _context.EventAuthors.Add(destEventAuthor);
                    }
                }

                // Step 2: Make sure all puzzles exist
                Dictionary <int, Puzzle> puzzleCloneMap = new Dictionary <int, Puzzle>();

                foreach (var sourcePuzzle in sourcePuzzles)
                {
                    // delete the puzzle if it exists
                    if (deletePuzzleIfPresent)
                    {
                        foreach (Puzzle p in _context.Puzzles.Where((p) => p.Event == Event && p.Name == sourcePuzzle.Name))
                        {
                            await PuzzleHelper.DeletePuzzleAsync(_context, p);
                        }
                    }

                    var destPuzzle = new Puzzle(sourcePuzzle);
                    destPuzzle.Event = Event;

                    puzzleCloneMap[sourcePuzzle.ID] = destPuzzle;
                    _context.Puzzles.Add(destPuzzle);
                }

                // Step 3: Save so that all our new objects have valid IDs
                await _context.SaveChangesAsync();

                // Step 4: Ancillary tables referring to puzzles
                foreach (var sourcePuzzle in sourcePuzzles)
                {
                    // PuzzleAuthors
                    foreach (PuzzleAuthors sourcePuzzleAuthor in _context.PuzzleAuthors.Where((p) => p.Puzzle == sourcePuzzle))
                    {
                        var destPuzzleAuthor = new PuzzleAuthors(sourcePuzzleAuthor);
                        destPuzzleAuthor.Puzzle = puzzleCloneMap[sourcePuzzleAuthor.Puzzle.ID];
                        _context.PuzzleAuthors.Add(destPuzzleAuthor);
                    }

                    // Responses
                    foreach (Response sourceResponse in _context.Responses.Where((r) => r.Puzzle == sourcePuzzle))
                    {
                        var destResponse = new Response(sourceResponse);
                        destResponse.Puzzle = puzzleCloneMap[sourceResponse.Puzzle.ID];
                        _context.Responses.Add(destResponse);
                    }

                    // Prerequisites
                    foreach (Prerequisites sourcePrerequisite in _context.Prerequisites.Where((r) => r.Puzzle == sourcePuzzle))
                    {
                        var destPrerequisite = new Prerequisites(sourcePrerequisite);
                        destPrerequisite.Puzzle       = puzzleCloneMap[sourcePrerequisite.Puzzle.ID];
                        destPrerequisite.Prerequisite = puzzleCloneMap[sourcePrerequisite.Prerequisite.ID];
                        _context.Prerequisites.Add(destPrerequisite);
                    }

                    // Hints
                    foreach (Hint sourceHint in _context.Hints.Where((h) => h.Puzzle == sourcePuzzle))
                    {
                        var destHint = new Hint(sourceHint);
                        destHint.Puzzle = puzzleCloneMap[sourceHint.Puzzle.ID];
                        _context.Hints.Add(destHint);

                        foreach (Team team in _context.Teams.Where(t => t.Event == Event))
                        {
                            _context.HintStatePerTeam.Add(new HintStatePerTeam()
                            {
                                Hint = destHint, TeamID = team.ID
                            });
                        }
                    }

                    // PuzzleStatePerTeam
                    foreach (Team team in _context.Teams.Where(t => t.Event == Event))
                    {
                        int  newPuzzleId           = puzzleCloneMap[sourcePuzzle.ID].ID;
                        bool hasPuzzleStatePerTeam = await(from pspt in _context.PuzzleStatePerTeam
                                                           where pspt.PuzzleID == newPuzzleId &&
                                                           pspt.TeamID == team.ID
                                                           select pspt).AnyAsync();
                        if (!hasPuzzleStatePerTeam)
                        {
                            PuzzleStatePerTeam newPspt = new PuzzleStatePerTeam()
                            {
                                TeamID = team.ID, PuzzleID = newPuzzleId
                            };
                            _context.PuzzleStatePerTeam.Add(newPspt);
                        }
                    }

                    // ContentFiles
                    foreach (ContentFile contentFile in _context.ContentFiles.Where((c) => c.Puzzle == sourcePuzzle))
                    {
                        ContentFile newFile = new ContentFile(contentFile);
                        newFile.Event  = Event;
                        newFile.Puzzle = puzzleCloneMap[contentFile.Puzzle.ID];
                        newFile.Url    = await FileManager.CloneBlobAsync(contentFile.ShortName, Event.ID, contentFile.Url);

                        _context.ContentFiles.Add(newFile);
                    }

                    // Pieces
                    foreach (Piece piece in _context.Pieces.Where((p) => p.Puzzle == sourcePuzzle))
                    {
                        Piece newPiece = new Piece(piece);
                        newPiece.Puzzle   = puzzleCloneMap[piece.PuzzleID];
                        newPiece.PuzzleID = puzzleCloneMap[piece.PuzzleID].ID; // unsure why I need this line for pieces but not others <shrug/>
                        _context.Pieces.Add(newPiece);
                    }
                }

                // Step 5: Final save and commit
                await _context.SaveChangesAsync();

                transaction.Commit();
            }

            return(RedirectToPage("./Details"));
        }
        public async Task <IActionResult> OnPostImportAsync()
        {
            // the BindProperty only binds the event ID, let's get the rest
            if (await _context.Events.Where((e) => e.ID == ImportEventID).FirstOrDefaultAsync() == null)
            {
                return(NotFound());
            }

            // verify that we're an admin of the import event. current event administratorship is already validated.
            if (!await _context.EventAdmins.Where(ea => ea.Event.ID == ImportEventID && ea.Admin == LoggedInUser).AnyAsync())
            {
                return(Forbid());
            }

            var sourceEventAuthors = await _context.EventAuthors.Where((a) => a.Event.ID == ImportEventID).ToListAsync();

            var sourcePuzzles = await _context.Puzzles.Where((p) => p.Event.ID == ImportEventID).ToListAsync();

            // TODO: replace this with a checkbox and sufficient danger warnings about duplicate titles
            bool deletePuzzleIfPresent = true;

            using (var transaction = _context.Database.BeginTransaction())
            {
                // Step 1: Make sure all authors exist
                foreach (var sourceEventAuthor in sourceEventAuthors)
                {
                    var destEventAuthor = await _context.EventAuthors.Where((e) => e.Event == Event && e.Author == sourceEventAuthor.Author).FirstOrDefaultAsync();

                    if (destEventAuthor == null)
                    {
                        destEventAuthor       = new EventAuthors(sourceEventAuthor);
                        destEventAuthor.Event = Event;
                        _context.EventAuthors.Add(destEventAuthor);
                    }
                }

                // Step 2: Make sure all puzzles exist
                Dictionary <int, Puzzle> puzzleCloneMap = new Dictionary <int, Puzzle>();

                foreach (var sourcePuzzle in sourcePuzzles)
                {
                    // delete the puzzle if it exists
                    if (deletePuzzleIfPresent)
                    {
                        foreach (Puzzle p in _context.Puzzles.Where((p) => p.Event == Event && p.Name == sourcePuzzle.Name))
                        {
                            await PuzzleHelper.DeletePuzzleAsync(_context, p);
                        }
                    }

                    var destPuzzle = new Puzzle(sourcePuzzle);
                    destPuzzle.Event = Event;

                    puzzleCloneMap[sourcePuzzle.ID] = destPuzzle;
                    _context.Puzzles.Add(destPuzzle);
                }

                // Step 3: Save so that all our new objects have valid IDs
                await _context.SaveChangesAsync();

                // Step 4: Ancillary tables referring to puzzles
                foreach (var sourcePuzzle in sourcePuzzles)
                {
                    // PuzzleAuthors
                    foreach (PuzzleAuthors sourcePuzzleAuthor in _context.PuzzleAuthors.Where((p) => p.Puzzle == sourcePuzzle))
                    {
                        var destPuzzleAuthor = new PuzzleAuthors(sourcePuzzleAuthor);
                        destPuzzleAuthor.Puzzle = puzzleCloneMap[sourcePuzzleAuthor.Puzzle.ID];
                        _context.PuzzleAuthors.Add(destPuzzleAuthor);
                    }

                    // Responses
                    foreach (Response sourceResponse in _context.Responses.Where((r) => r.Puzzle == sourcePuzzle))
                    {
                        var destResponse = new Response(sourceResponse);
                        destResponse.Puzzle = puzzleCloneMap[sourceResponse.Puzzle.ID];
                        _context.Responses.Add(destResponse);
                    }

                    // Prerequisites
                    foreach (Prerequisites sourcePrerequisite in _context.Prerequisites.Where((r) => r.Puzzle == sourcePuzzle))
                    {
                        var destPrerequisite = new Prerequisites(sourcePrerequisite);
                        destPrerequisite.Puzzle       = puzzleCloneMap[sourcePrerequisite.Puzzle.ID];
                        destPrerequisite.Prerequisite = puzzleCloneMap[sourcePrerequisite.Prerequisite.ID];
                        _context.Prerequisites.Add(destPrerequisite);
                    }

                    // Hints
                    foreach (Hint sourceHint in _context.Hints.Where((h) => h.Puzzle == sourcePuzzle))
                    {
                        var destHint = new Hint(sourceHint);
                        destHint.Puzzle = puzzleCloneMap[sourceHint.Puzzle.ID];
                        _context.Hints.Add(destHint);
                    }

                    // TODO: ContentFiles
                }

                // Step 5: Final save and commit
                await _context.SaveChangesAsync();

                transaction.Commit();
            }

            return(RedirectToPage("./Index"));
        }
Esempio n. 5
0
        public async Task <IActionResult> OnGetAsync()
        {
            List <Puzzle> puzzles = await PuzzleHelper.GetPuzzles(_context, Event, LoggedInUser, EventRole);

            ILookup <int, string> puzzleAuthors = (await(from author in _context.PuzzleAuthors
                                                         where author.Puzzle.Event == Event
                                                         select author).ToListAsync()).ToLookup(author => author.PuzzleID, author => author.Author.Name);
            Dictionary <int, ContentFile> puzzleFiles = await(from file in _context.ContentFiles
                                                              where file.Event == Event && file.FileType == ContentFileType.Puzzle
                                                              select file).ToDictionaryAsync(file => file.PuzzleID);
            Dictionary <int, ContentFile> puzzleAnswers = await(from file in _context.ContentFiles
                                                                where file.Event == Event && file.FileType == ContentFileType.Answer
                                                                select file).ToDictionaryAsync(file => file.PuzzleID);
            ILookup <int, string> puzzlePrereqs = (await(from prerequisite in _context.Prerequisites
                                                         where prerequisite.Puzzle.Event == Event
                                                         select prerequisite).ToListAsync()).ToLookup(prerequisite => prerequisite.PuzzleID, prerequisite => prerequisite.Prerequisite.Name);

            HashSet <int> puzzlesWithSolutions = (await(from response in _context.Responses
                                                        where response.Puzzle.Event == Event && response.IsSolution
                                                        select response.PuzzleID).ToListAsync()).ToHashSet();
            Dictionary <int, int> puzzleResponses = await(from response in _context.Responses
                                                          where response.Puzzle.Event == Event
                                                          group response by response.PuzzleID into responseList
                                                          select new { Puzzle = responseList.Key, ResponseCount = responseList.Count() }).ToDictionaryAsync(x => x.Puzzle, x => x.ResponseCount);

            ILookup <int, int> puzzleHints = (await(from hint in _context.Hints
                                                    where hint.Puzzle.Event == Event
                                                    select new { PuzzleId = hint.PuzzleID, Cost = hint.Cost }).ToListAsync()).ToLookup(hint => hint.PuzzleId, hint => hint.Cost);

            PuzzleData = new List <PuzzleView>();
            foreach (Puzzle puzzle in puzzles)
            {
                IEnumerable <string> authors = puzzleAuthors[puzzle.ID];
                puzzleFiles.TryGetValue(puzzle.ID, out ContentFile puzzleFile);
                puzzleAnswers.TryGetValue(puzzle.ID, out ContentFile puzzleAnswer);
                IEnumerable <string> prereqs = puzzlePrereqs[puzzle.ID];
                puzzleResponses.TryGetValue(puzzle.ID, out int responses);

                int totalHintCostThisPuzzle = 0;
                int hintsCountThisPuzzle    = 0;

                if (puzzleHints.Contains(puzzle.ID))
                {
                    IEnumerable <int> hints = puzzleHints[puzzle.ID];
                    int totalDiscount       = 0;

                    hintsCountThisPuzzle = hints.Count();

                    // positive hints cost what they say they cost.
                    // negative hints apply discounts to each other, find the most negative hint.
                    foreach (int cost in hints)
                    {
                        totalDiscount            = Math.Min(totalDiscount, cost);
                        totalHintCostThisPuzzle += Math.Max(0, cost);
                    }

                    // add the total available discount to the cost.
                    totalHintCostThisPuzzle += Math.Abs(totalDiscount);
                }

                PuzzleData.Add(new PuzzleView()
                {
                    Puzzle             = puzzle,
                    Authors            = authors != null ? string.Join(", ", authors) : "",
                    PuzzleFile         = puzzleFile,
                    AnswerFile         = puzzleAnswer,
                    Prerequisites      = prereqs != null ? string.Join(", ", prereqs.OrderBy(prereq => prereq)) : "",
                    PrerequisitesCount = prereqs != null ? prereqs.Count() : 0,
                    Responses          = new ResponseData {
                        ResponseCount = responses, HasAnswer = puzzlesWithSolutions.Contains(puzzle.ID)
                    },
                    Hints         = hintsCountThisPuzzle,
                    TotalHintCost = totalHintCostThisPuzzle
                });

                TotalHints    += hintsCountThisPuzzle;
                TotalHintCost += totalHintCostThisPuzzle;
            }

            return(Page());
        }