Exemplo n.º 1
0
 public EditModel(
     UserManager <IdentityUser> userManager,
     PuzzleServerContext context)
 {
     _userManager = userManager;
     _context     = context;
 }
Exemplo n.º 2
0
        /// <summary>
        /// Get a read-write query of puzzle state without any missing values. This is the most performant query, but its data is incomplete. Use with caution!
        /// </summary>
        /// <param name="context">The puzzle DB context</param>
        /// <param name="eventObj">The event we are querying from</param>
        /// <param name="puzzle">The puzzle; if null, get all puzzles in the event.</param>
        /// <param name="team">The team; if null, get all the teams in the event.</param>
        /// <returns>A query of PuzzleStatePerTeam objects that currently exist in the table.</returns>
        public static IQueryable <PuzzleStatePerTeam> GetSparseQuery(PuzzleServerContext context, Event eventObj, Puzzle puzzle, Team team)
        {
            if (context == null)
            {
                throw new ArgumentNullException("Context required.");
            }

            if (eventObj == null)
            {
                throw new ArgumentNullException("Event required.");
            }

            if (puzzle != null && team != null)
            {
                return(context.PuzzleStatePerTeam.Where(state => state.Puzzle == puzzle && state.Team == team));
            }

            if (puzzle != null)
            {
                return(context.PuzzleStatePerTeam.Where(state => state.Puzzle == puzzle));
            }

            if (team != null)
            {
                return(context.PuzzleStatePerTeam.Where(state => state.Team == team));
            }

            return(context.PuzzleStatePerTeam.Where(state => state.Puzzle.Event == eventObj));
        }
        public static async Task UpdateTeamsWhoSentResponse(PuzzleServerContext context, Response response)
        {
            using (IDbContextTransaction transaction = context.Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
            {
                var submissionsThatMatchResponse = await(from PuzzleStatePerTeam pspt in context.PuzzleStatePerTeam
                                                         join Submission sub in context.Submissions on pspt.Team equals sub.Team
                                                         where pspt.PuzzleID == response.PuzzleID &&
                                                         sub.SubmissionText == response.SubmittedText
                                                         select new { State = pspt, Submission = sub }).ToListAsync();

                if (submissionsThatMatchResponse.Count > 0)
                {
                    Puzzle puzzle = await context.Puzzles.Where((p) => p.ID == response.PuzzleID).FirstOrDefaultAsync();

                    foreach (var s in submissionsThatMatchResponse)
                    {
                        s.Submission.Response = response;
                        context.Attach(s.Submission).State = EntityState.Modified;

                        if (response.IsSolution && s.State.SolvedTime == null)
                        {
                            await SetSolveStateAsync(context, puzzle.Event, puzzle, s.State.Team, s.Submission.TimeSubmitted);
                        }
                    }

                    await context.SaveChangesAsync();

                    transaction.Commit();
                }
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Helper for deleting teams that correctly deletes dependent objects
        /// </summary>
        /// <param name="team">Team to delete</param>
        public static async Task DeleteTeamAsync(PuzzleServerContext context, Team team)
        {
            var puzzleStates = from puzzleState in context.PuzzleStatePerTeam
                               where puzzleState.TeamID == team.ID
                               select puzzleState;

            context.PuzzleStatePerTeam.RemoveRange(puzzleStates);

            var hintStates = from hintState in context.HintStatePerTeam
                             where hintState.TeamID == team.ID
                             select hintState;

            context.HintStatePerTeam.RemoveRange(hintStates);

            var submissions = from submission in context.Submissions
                              where submission.Team == team
                              select submission;

            context.Submissions.RemoveRange(submissions);

            var annotations = from annotation in context.Annotations
                              where annotation.Team == team
                              select annotation;

            context.Annotations.RemoveRange(annotations);

            context.Teams.Remove(team);

            await context.SaveChangesAsync();
        }
Exemplo n.º 5
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment() && String.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")))
            {
                app.UseBrowserLink();
                app.UseDeveloperExceptionPage();
                PuzzleServerContext.UpdateDatabase(app);
            }
            else if (env.IsStaging() && Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") == "PuzzleServerTestDeploy")
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
                PuzzleServerContext.UpdateDatabase(app);
            }
            else if (env.IsProduction() && (Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") == "puzzlehunt" || Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") == "puzzleday"))
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();

            // According to the Identity Scaffolding readme the order of the following calls matters
            // Must be UseStaticFiles, UseAuthentication, UseMvc
            app.UseStaticFiles();
            app.UseAuthentication();
            app.UseMvc();
        }
        /// <summary>
        /// Set the solve state of some puzzle state records. In the course of setting the state, instantiate any state records that are missing on the server.
        /// </summary>
        /// <param name="context">The puzzle DB context</param>
        /// <param name="eventObj">The event we are querying from</param>
        /// <param name="puzzle">
        ///     The puzzle; if null, get all puzzles in the event.
        /// </param>
        /// <param name="team">
        ///     The team; if null, get all the teams in the event.
        /// </param>
        /// <param name="value">The solve time (null if unsolving)</param>
        /// <param name="author"></param>
        /// <returns>
        ///     A task that can be awaited for the solve/unsolve operation
        /// </returns>
        public static async Task SetSolveStateAsync(
            PuzzleServerContext context,
            Event eventObj,
            Puzzle puzzle,
            Team team,
            DateTime?value,
            PuzzleUser author = null)
        {
            IQueryable <PuzzleStatePerTeam> statesQ = PuzzleStateHelper
                                                      .GetFullReadWriteQuery(context, eventObj, puzzle, team, author);

            List <PuzzleStatePerTeam> states = await statesQ.ToListAsync();

            for (int i = 0; i < states.Count; i++)
            {
                // Only allow solved time to be modified if it is being marked as unsolved (set to null) or if it is being solved for the first time
                if (value == null || states[i].SolvedTime == null)
                {
                    // Unlock puzzles when solving them
                    if (value != null && states[i].UnlockedTime == null)
                    {
                        states[i].UnlockedTime = value;
                    }

                    states[i].SolvedTime = value;
                }
            }

            // Award hint coins
            if (value != null && puzzle != null && puzzle.HintCoinsForSolve != 0)
            {
                if (team != null)
                {
                    team.HintCoinCount += puzzle.HintCoinsForSolve;
                }
                else
                {
                    var allTeams = from Team curTeam in context.Teams
                                   where curTeam.Event == eventObj
                                   select curTeam;
                    foreach (Team curTeam in allTeams)
                    {
                        curTeam.HintCoinCount += puzzle.HintCoinsForSolve;
                    }
                }
            }

            await context.SaveChangesAsync();

            // if this puzzle got solved, look for others to unlock
            if (puzzle != null && value != null)
            {
                await UnlockAnyPuzzlesThatThisSolveUnlockedAsync(context,
                                                                 eventObj,
                                                                 puzzle,
                                                                 team,
                                                                 value.Value);
            }
        }
Exemplo n.º 7
0
 /// <summary>
 /// Returns the the team for the given player
 /// </summary>
 /// <param name="dbContext">Current PuzzleServerContext</param>
 /// <param name="thisEvent">The event that's being checked</param>
 /// <param name="user">The user being checked</param>
 /// <returns>The user's team for this event</returns>
 public static async Task <Team> GetTeamForPlayer(PuzzleServerContext dbContext, Event thisEvent, PuzzleUser user)
 {
     if (user == null)
     {
         return(null);
     }
     return(await dbContext.TeamMembers.Where(t => t.Member.ID == user.ID && t.Team.Event.ID == thisEvent.ID).Select(t => t.Team).FirstOrDefaultAsync());
 }
Exemplo n.º 8
0
 public static async Task SetTeamQualificationAsync(
     PuzzleServerContext context,
     Team team,
     bool value)
 {
     team.IsDisqualified = value;
     await context.SaveChangesAsync();
 }
        /// <summary>
        /// Get the solved status of a single puzzle.
        /// Do not use if you want to get status of many puzzles as it will be very inefficient.
        /// </summary>
        /// <returns></returns>
        public static async Task <bool> IsPuzzleSolved(PuzzleServerContext context, int puzzleID, int teamID)
        {
            DateTime?solved = await(from PuzzleStatePerTeam pspt in context.PuzzleStatePerTeam
                                    where pspt.PuzzleID == puzzleID && pspt.TeamID == teamID
                                    select pspt.SolvedTime).FirstOrDefaultAsync();

            return(solved != null);
        }
Exemplo n.º 10
0
 public IndexModel(
     UserManager <IdentityUser> userManager,
     SignInManager <IdentityUser> signInManager,
     PuzzleServerContext context)
 {
     _userManager   = userManager;
     _signInManager = signInManager;
     _context       = context;
 }
Exemplo n.º 11
0
        public static IQueryable <Puzzle> PuzzlesCausingGlobalLockout(
            PuzzleServerContext context,
            Event eventObj,
            Team team)
        {
            DateTime now = DateTime.UtcNow;

            return(PuzzleStateHelper.GetSparseQuery(context, eventObj, null, team)
                   .Where(state => state.SolvedTime == null && state.UnlockedTime != null && state.Puzzle.MinutesOfEventLockout != 0 && state.UnlockedTime.Value + TimeSpan.FromMinutes(state.Puzzle.MinutesOfEventLockout) > now)
                   .Select((s) => s.Puzzle));
        }
 public ExternalLoginModel(
     SignInManager <IdentityUser> signInManager,
     UserManager <IdentityUser> userManager,
     ILogger <ExternalLoginModel> logger,
     PuzzleServerContext context)
 {
     _signInManager = signInManager;
     _userManager   = userManager;
     _logger        = logger;
     _context       = context;
 }
Exemplo n.º 13
0
        /// <summary>
        /// Adds a user to a team after performing a number of checks to make sure that the change is valid
        /// </summary>
        /// <param name="context">The context to update</param>
        /// <param name="Event">The event that the team is for</param>
        /// <param name="EventRole">The event role of the user that is making this change</param>
        /// <param name="teamId">The id of the team the player should be added to</param>
        /// <param name="userId">The user that should be added to the team</param>
        /// <returns>
        /// A tuple where the first element is a boolean that indicates whether the player was successfully
        /// added to the team and the second element is a message to display that explains the error in the
        /// case where the user was not successfully added to the team
        /// </returns>
        public static async Task <Tuple <bool, string> > AddMemberAsync(PuzzleServerContext context, Event Event, EventRole EventRole, int teamId, int userId)
        {
            Team team = await context.Teams.FirstOrDefaultAsync(m => m.ID == teamId);

            if (team == null)
            {
                return(new Tuple <bool, string>(false, $"Could not find team with ID '{teamId}'. Check to make sure the team hasn't been removed."));
            }

            var currentTeamMembers = await context.TeamMembers.Where(members => members.Team.ID == team.ID).ToListAsync();

            if (currentTeamMembers.Count >= Event.MaxTeamSize && EventRole != EventRole.admin)
            {
                return(new Tuple <bool, string>(false, $"The team '{team.Name}' is full."));
            }

            PuzzleUser user = await context.PuzzleUsers.FirstOrDefaultAsync(m => m.ID == userId);

            if (user == null)
            {
                return(new Tuple <bool, string>(false, $"Could not find user with ID '{userId}'. Check to make sure the user hasn't been removed."));
            }

            if (user.EmployeeAlias == null && currentTeamMembers.Where((m) => m.Member.EmployeeAlias == null).Count() >= Event.MaxExternalsPerTeam)
            {
                return(new Tuple <bool, string>(false, $"The team '{team.Name}' is already at its maximum count of non-employee players, and '{user.Email}' has no registered alias."));
            }

            if (await(from teamMember in context.TeamMembers
                      where teamMember.Member == user &&
                      teamMember.Team.Event == Event
                      select teamMember).AnyAsync())
            {
                return(new Tuple <bool, string>(false, $"'{user.Email}' is already on a team in this event."));
            }

            TeamMembers Member = new TeamMembers();

            Member.Team   = team;
            Member.Member = user;

            // Remove any applications the user might have started for this event
            var allApplications = from app in context.TeamApplications
                                  where app.Player == user &&
                                  app.Team.Event == Event
                                  select app;

            context.TeamApplications.RemoveRange(allApplications);

            context.TeamMembers.Add(Member);
            await context.SaveChangesAsync();

            return(new Tuple <bool, string>(true, ""));
        }
Exemplo n.º 14
0
        public static async Task CheckForTimedUnlocksAsync(
            PuzzleServerContext context,
            Event eventObj,
            Team team)
        {
            DateTime expiry;

            lock (TimedUnlockExpiryCache)
            {
                // throttle this by an expiry interval before we do anything even remotely expensive
                if (TimedUnlockExpiryCache.TryGetValue(team.ID, out expiry) && expiry >= DateTime.UtcNow)
                {
                    return;
                }
            }

            DateTime now = DateTime.UtcNow;

            // do the unlocks in a loop.
            // The loop will catch cascading unlocks, e.g. if someone does not hit the site between 11:59 and 12:31, catch up to the 12:30 unlocks immediately.
            while (true)
            {
                var puzzlesToSolveByTime = await PuzzleStateHelper.GetSparseQuery(context, eventObj, null, team)
                                           .Where(state => state.SolvedTime == null && state.UnlockedTime != null && state.Puzzle.MinutesToAutomaticallySolve != null && state.UnlockedTime.Value + TimeSpan.FromMinutes(state.Puzzle.MinutesToAutomaticallySolve.Value) <= now)
                                           .Select((state) => new { Puzzle = state.Puzzle, UnlockedTime = state.UnlockedTime.Value })
                                           .ToListAsync();

                foreach (var state in puzzlesToSolveByTime)
                {
                    // mark solve time as when the puzzle was supposed to complete, so cascading unlocks work properly.
                    await PuzzleStateHelper.SetSolveStateAsync(context, eventObj, state.Puzzle, team, state.UnlockedTime + TimeSpan.FromMinutes(state.Puzzle.MinutesToAutomaticallySolve.Value));
                }

                // get out of the loop if we did nothing
                if (puzzlesToSolveByTime.Count == 0)
                {
                    break;
                }
            }

            lock (TimedUnlockExpiryCache)
            {
                // effectively, expiry = Math.Max(DateTime.UtcNow, LastGlobalExpiry) + ClosestExpirySpacing - if you could use Math.Max on DateTime
                expiry = DateTime.UtcNow;
                if (expiry < LastGlobalExpiry)
                {
                    expiry = LastGlobalExpiry;
                }
                expiry += ClosestExpirySpacing;

                TimedUnlockExpiryCache[team.ID] = expiry;
                LastGlobalExpiry = expiry;
            }
        }
Exemplo n.º 15
0
        /// <summary>
        /// Set the unlock state of some puzzle state records. In the course of setting the state, instantiate any state records that are missing on the server.
        /// </summary>
        /// <param name="context">The puzzle DB context</param>
        /// <param name="eventObj">The event we are querying from</param>
        /// <param name="puzzle">The puzzle; if null, get all puzzles in the event.</param>
        /// <param name="team">The team; if null, get all the teams in the event.</param>
        /// <param name="value">The unlock time (null if relocking)</param>
        /// <returns>A task that can be awaited for the unlock/lock operation</returns>
        public static async Task SetUnlockStateAsync(PuzzleServerContext context, Event eventObj, Puzzle puzzle, Team team, DateTime?value, PuzzleUser author = null)
        {
            IQueryable <PuzzleStatePerTeam> statesQ = await PuzzleStateHelper.GetFullReadWriteQueryAsync(context, eventObj, puzzle, team, author);

            List <PuzzleStatePerTeam> states = await statesQ.ToListAsync();

            for (int i = 0; i < states.Count; i++)
            {
                states[i].UnlockedTime = value;
            }
            await context.SaveChangesAsync();
        }
            public async Task BindModelAsync(ModelBindingContext bindingContext)
            {
                string eventId = bindingContext.ActionContext.RouteData.Values["eventId"] as string;

                PuzzleServerContext puzzleServerContext = bindingContext.HttpContext.RequestServices.GetService <PuzzleServerContext>();

                Event eventObj = await EventHelper.GetEventFromEventId(puzzleServerContext, eventId);

                if (eventObj != null)
                {
                    bindingContext.Result = ModelBindingResult.Success(eventObj);
                }
            }
Exemplo n.º 17
0
        public static async Task <Event> GetEventFromContext(AuthorizationHandlerContext context)
        {
            Event result = null;

            if (context.Resource is AuthorizationFilterContext filterContext)
            {
                PuzzleServerContext puzzleServerContext = (PuzzleServerContext)filterContext.HttpContext.RequestServices.GetService(typeof(PuzzleServerContext));
                string eventId = filterContext.RouteData.Values["eventId"] as string;

                result = await EventHelper.GetEventFromEventId(puzzleServerContext, eventId);
            }

            return(result);
        }
Exemplo n.º 18
0
        /// <summary>
        /// Set the solve state of some puzzle state records. In the course of setting the state, instantiate any state records that are missing on the server.
        /// </summary>
        /// <param name="context">The puzzle DB context</param>
        /// <param name="eventObj">The event we are querying from</param>
        /// <param name="puzzle">
        ///     The puzzle; if null, get all puzzles in the event.
        /// </param>
        /// <param name="team">
        ///     The team; if null, get all the teams in the event.
        /// </param>
        /// <param name="value">The solve time (null if unsolving)</param>
        /// <param name="author"></param>
        /// <returns>
        ///     A task that can be awaited for the solve/unsolve operation
        /// </returns>
        public static async Task SetSolveStateAsync(
            PuzzleServerContext context,
            Event eventObj,
            Puzzle puzzle,
            Team team,
            DateTime?value,
            PuzzleUser author = null)
        {
            IQueryable <PuzzleStatePerTeam> statesQ = await PuzzleStateHelper
                                                      .GetFullReadWriteQueryAsync(context, eventObj, puzzle, team, author);

            List <PuzzleStatePerTeam> states = await statesQ.ToListAsync();

            for (int i = 0; i < states.Count; i++)
            {
                states[i].SolvedTime = value;
            }

            // Award hint coins
            if (value != null && puzzle != null && puzzle.HintCoinsForSolve != 0)
            {
                if (team != null)
                {
                    team.HintCoinCount += puzzle.HintCoinsForSolve;
                }
                else
                {
                    var allTeams = from Team curTeam in context.Teams
                                   where curTeam.Event == eventObj
                                   select curTeam;
                    foreach (Team curTeam in allTeams)
                    {
                        curTeam.HintCoinCount += puzzle.HintCoinsForSolve;
                    }
                }
            }

            await context.SaveChangesAsync();

            // if this puzzle got solved, look for others to unlock
            if (value != null)
            {
                await UnlockAnyPuzzlesThatThisSolveUnlockedAsync(context,
                                                                 eventObj,
                                                                 puzzle,
                                                                 team,
                                                                 value.Value);
            }
        }
Exemplo n.º 19
0
        /// <summary>
        /// Set the unlock state of some puzzle state records. In the course of setting the state, instantiate any state records that are missing on the server.
        /// </summary>
        /// <param name="context">The puzzle DB context</param>
        /// <param name="eventObj">The event we are querying from</param>
        /// <param name="puzzle">The puzzle; if null, get all puzzles in the event.</param>
        /// <param name="team">The team; if null, get all the teams in the event.</param>
        /// <param name="value">The unlock time (null if relocking)</param>
        /// <returns>A task that can be awaited for the unlock/lock operation</returns>
        public static async Task SetUnlockStateAsync(PuzzleServerContext context, Event eventObj, Puzzle puzzle, Team team, DateTime?value, PuzzleUser author = null)
        {
            IQueryable <PuzzleStatePerTeam> statesQ = PuzzleStateHelper.GetFullReadWriteQuery(context, eventObj, puzzle, team, author);
            List <PuzzleStatePerTeam>       states  = await statesQ.ToListAsync();

            for (int i = 0; i < states.Count; i++)
            {
                // Only allow unlock time to be modified if we were relocking it (setting it to null) or unlocking it for the first time
                if (value == null || states[i].UnlockedTime == null)
                {
                    states[i].UnlockedTime = value;
                }
            }
            await context.SaveChangesAsync();
        }
Exemplo n.º 20
0
        public static async Task <Team> GetTeamFromContext(AuthorizationHandlerContext context)
        {
            if (context.Resource is AuthorizationFilterContext filterContext)
            {
                string teamIdAsString = filterContext.RouteData.Values["teamId"] as string;

                if (Int32.TryParse(teamIdAsString, out int teamId))
                {
                    PuzzleServerContext puzzleServerContext = (PuzzleServerContext)filterContext.HttpContext.RequestServices.GetService(typeof(PuzzleServerContext));
                    return(await puzzleServerContext.Teams.Where(e => e.ID == teamId).FirstOrDefaultAsync());
                }
            }

            return(null);
        }
Exemplo n.º 21
0
        public static async Task <List <Puzzle> > GetPuzzles(PuzzleServerContext context, Event Event, PuzzleUser user, EventRole role)
        {
            IQueryable <Puzzle> query;

            if (role == EventRole.admin)
            {
                query = context.Puzzles.Where(p => p.Event == Event);
            }
            else
            {
                query = UserEventHelper.GetPuzzlesForAuthorAndEvent(context, Event, user);
            }

            return(await query.OrderBy(p => p.Group).ThenBy(p => p.OrderInGroup).ThenBy(p => p.Name).ToListAsync());
        }
Exemplo n.º 22
0
        /// <summary>
        /// Returns the event that matches a given eventId
        /// </summary>
        /// <param name="eventId">The eventId from the URL - either the ID of an event of the UrlString of an event.</param>
        public static async Task <Event> GetEventFromEventId(PuzzleServerContext puzzleServerContext, string eventId)
        {
            Event result = null;

            // first, lookup by UrlString - this is the friendly name
            result = await puzzleServerContext.Events.Where(e => e.UrlString == eventId).FirstOrDefaultAsync();

            // otherwise, look up by int for legacy event support
            // TODO: Delete when people have cleaned up their DBs
            if (result == null && Int32.TryParse(eventId, out int eventIdAsInt))
            {
                result = await puzzleServerContext.Events.Where(e => e.ID == eventIdAsInt).FirstOrDefaultAsync();
            }

            return(result);
        }
Exemplo n.º 23
0
        /// <summary>
        /// Returns the event that matches a given eventId
        /// </summary>
        /// <param name="eventId">The eventId from the URL - either the ID of an event of the UrlString of an event.</param>
        public static async Task <Event> GetEventFromEventId(PuzzleServerContext puzzleServerContext, string eventId)
        {
            Event result = null;

            // Decide whether to look up by friendly string or ID
            if (int.TryParse(eventId, out int eventIdAsInt))
            {
                result = await puzzleServerContext.Events.Where(e => e.ID == eventIdAsInt).FirstOrDefaultAsync();
            }
            else
            {
                // first, lookup by UrlString - this is the friendly name
                result = await puzzleServerContext.Events.Where(e => e.UrlString == eventId).FirstOrDefaultAsync();
            }

            return(result);
        }
Exemplo n.º 24
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseBrowserLink();
                app.UseDeveloperExceptionPage();
                PuzzleServerContext.UpdateDatabase(app);
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();

            app.UseMvc();
        }
Exemplo n.º 25
0
        /// <summary>
        /// Helper for deleting puzzles that correctly deletes dependent objects
        /// </summary>
        /// <param name="puzzle">Puzzle to delete</param>
        public static async Task DeletePuzzleAsync(PuzzleServerContext context, Puzzle puzzle)
        {
            // Delete all files associated with this puzzle
            foreach (ContentFile content in puzzle.Contents)
            {
                await FileManager.DeleteBlobAsync(content.Url);
            }

            // Delete all Prerequisites where this puzzle depends on or is depended upon by another puzzle
            foreach (Prerequisites thisPrerequisite in context.Prerequisites.Where((r) => r.Puzzle == puzzle || r.Prerequisite == puzzle))
            {
                context.Prerequisites.Remove(thisPrerequisite);
            }

            context.Puzzles.Remove(puzzle);
            await context.SaveChangesAsync();
        }
Exemplo n.º 26
0
        /// <summary>
        /// Helper for deleting teams that correctly deletes dependent objects
        /// </summary>
        /// <param name="team">Team to delete</param>
        public static async Task DeleteTeamAsync(PuzzleServerContext context, Team team, bool sendEmail = true)
        {
            var memberEmails = await(from teamMember in context.TeamMembers
                                     where teamMember.Team == team
                                     select teamMember.Member.Email).ToListAsync();
            var applicationEmails = await(from app in context.TeamApplications
                                          where app.Team == team
                                          select app.Player.Email).ToListAsync();

            var puzzleStates = from puzzleState in context.PuzzleStatePerTeam
                               where puzzleState.TeamID == team.ID
                               select puzzleState;

            context.PuzzleStatePerTeam.RemoveRange(puzzleStates);

            var hintStates = from hintState in context.HintStatePerTeam
                             where hintState.TeamID == team.ID
                             select hintState;

            context.HintStatePerTeam.RemoveRange(hintStates);

            var submissions = from submission in context.Submissions
                              where submission.Team == team
                              select submission;

            context.Submissions.RemoveRange(submissions);

            var annotations = from annotation in context.Annotations
                              where annotation.Team == team
                              select annotation;

            context.Annotations.RemoveRange(annotations);

            context.Teams.Remove(team);

            await context.SaveChangesAsync();

            if (sendEmail)
            {
                MailHelper.Singleton.SendPlaintextBcc(memberEmails.Union(applicationEmails),
                                                      $"{team.Event.Name}: Team '{team.Name}' has been deleted",
                                                      $"Sorry! You can apply to another team if you wish, or create your own.");
            }
        }
Exemplo n.º 27
0
        public static async Task CheckForTimedUnlocksAsync(
            PuzzleServerContext context,
            Event eventObj,
            Team team)
        {
            DateTime expiry;

            lock (TimedUnlockExpiryCache)
            {
                // throttle this by an expiry interval before we do anything even remotely expensive
                if (TimedUnlockExpiryCache.TryGetValue(team.ID, out expiry) && expiry >= DateTime.UtcNow)
                {
                    return;
                }
            }

            DateTime now = DateTime.UtcNow;
            var      puzzlesToSolveByTime = await PuzzleStateHelper.GetSparseQuery(context, eventObj, null, team)
                                            .Where(state => state.SolvedTime == null && state.UnlockedTime != null && state.Puzzle.MinutesToAutomaticallySolve != null && state.UnlockedTime.Value + TimeSpan.FromMinutes(state.Puzzle.MinutesToAutomaticallySolve.Value) <= now)
                                            .Select(state => state.Puzzle)
                                            .ToListAsync();

            foreach (Puzzle puzzle in puzzlesToSolveByTime)
            {
                await PuzzleStateHelper.SetSolveStateAsync(context, eventObj, puzzle, team, DateTime.UtcNow);
            }

            lock (TimedUnlockExpiryCache)
            {
                // effectively, expiry = Math.Max(DateTime.UtcNow, LastGlobalExpiry) + ClosestExpirySpacing - if you could use Math.Max on DateTime
                expiry = DateTime.UtcNow;
                if (expiry < LastGlobalExpiry)
                {
                    expiry = LastGlobalExpiry;
                }
                expiry += ClosestExpirySpacing;

                TimedUnlockExpiryCache[team.ID] = expiry;
                LastGlobalExpiry = expiry;
            }
        }
Exemplo n.º 28
0
        /// <summary>
        /// Get a read-only query of puzzle state. You won't be able to write to these values, but the query will be resilient to state records that are missing on the server.
        /// </summary>
        /// <param name="context">The puzzle DB context</param>
        /// <param name="eventObj">The event we are querying from</param>
        /// <param name="puzzle">The puzzle; if null, get all puzzles in the event.</param>
        /// <param name="team">The team; if null, get all the teams in the event.</param>
        /// <param name="author">The author; if null get puzzles matching other criteria by all authors</param>
        /// <returns>A query of PuzzleStatePerTeam objects that can be sorted and instantiated, but you can't edit the results.</returns>
        public static IQueryable <PuzzleStatePerTeam> GetFullReadOnlyQuery(PuzzleServerContext context, Event eventObj, Puzzle puzzle, Team team, PuzzleUser author = null)
        {
            if (context == null)
            {
                throw new ArgumentNullException("Context required.");
            }

            if (eventObj == null)
            {
                throw new ArgumentNullException("Event required.");
            }

            if (puzzle != null && team != null)
            {
                return(context.PuzzleStatePerTeam
                       .Where(state => state.Puzzle == puzzle && state.Team == team));
            }

            if (puzzle != null)
            {
                return(context.PuzzleStatePerTeam.Where(state => state.Puzzle == puzzle));
            }

            if (team != null)
            {
                if (author != null)
                {
                    return(from state in context.PuzzleStatePerTeam
                           join auth in context.PuzzleAuthors on state.PuzzleID equals auth.PuzzleID
                           where state.Team == team &&
                           auth.Author == author
                           select state);
                }
                else
                {
                    return(context.PuzzleStatePerTeam.Where(state => state.Team == team));
                }
            }

            throw new NotImplementedException("Full event query is NYI and may never be needed; use the sparse one");
        }
Exemplo n.º 29
0
        public static async Task UpdateTeamsWhoSentResponse(PuzzleServerContext context, Response response)
        {
            using (IDbContextTransaction transaction = context.Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
            {
                var submissionsThatMatchResponse = await(from PuzzleStatePerTeam pspt in context.PuzzleStatePerTeam
                                                         join Submission sub in context.Submissions on pspt.Team equals sub.Team
                                                         where pspt.PuzzleID == response.PuzzleID &&
                                                         sub.PuzzleID == response.PuzzleID &&
                                                         sub.SubmissionText == response.SubmittedText
                                                         select new { State = pspt, Submission = sub }).ToListAsync();

                if (submissionsThatMatchResponse.Count > 0)
                {
                    Puzzle puzzle = await context.Puzzles.Where((p) => p.ID == response.PuzzleID).FirstOrDefaultAsync();

                    foreach (var s in submissionsThatMatchResponse)
                    {
                        s.Submission.Response = response;
                        context.Attach(s.Submission).State = EntityState.Modified;

                        if (response.IsSolution && s.State.SolvedTime == null)
                        {
                            await SetSolveStateAsync(context, puzzle.Event, puzzle, s.State.Team, s.Submission.TimeSubmitted);
                        }
                    }

                    await context.SaveChangesAsync();

                    transaction.Commit();

                    var teamMembers = await(from TeamMembers tm in context.TeamMembers
                                            join Submission sub in context.Submissions on tm.Team equals sub.Team
                                            where sub.PuzzleID == response.PuzzleID && sub.SubmissionText == response.SubmittedText
                                            select tm.Member.Email).ToListAsync();
                    MailHelper.Singleton.SendPlaintextBcc(teamMembers,
                                                          $"{puzzle.Event.Name}: {response.Puzzle.Name} Response updated for '{response.SubmittedText}'",
                                                          $"The new response for this submission is: '{response.ResponseText}'.");
                }
            }
        }
Exemplo n.º 30
0
        /// <summary>
        /// Get a read-write query of puzzle state without any missing values. This is the most performant query, but its data is incomplete. Use with caution!
        /// </summary>
        /// <param name="context">The puzzle DB context</param>
        /// <param name="eventObj">The event we are querying from</param>
        /// <param name="puzzle">The puzzle; if null, get all puzzles in the event.</param>
        /// <param name="team">The team; if null, get all the teams in the event.</param>
        /// <returns>A query of PuzzleStatePerTeam objects that currently exist in the table.</returns>
        public static IQueryable <PuzzleStatePerTeam> GetSparseQuery(PuzzleServerContext context, Event eventObj, Puzzle puzzle, Team team, PuzzleUser author = null)
        {
            if (context == null)
            {
                throw new ArgumentNullException("Context required.");
            }

            if (eventObj == null)
            {
                throw new ArgumentNullException("Event required.");
            }

            if (puzzle != null && team != null)
            {
                return(context.PuzzleStatePerTeam.Where(state => state.Puzzle == puzzle && state.Team == team));
            }

            if (puzzle != null)
            {
                return(context.PuzzleStatePerTeam.Where(state => state.Puzzle == puzzle));
            }

            if (team != null)
            {
                if (author != null)
                {
                    return(from state in context.PuzzleStatePerTeam
                           join auth in context.PuzzleAuthors on state.PuzzleID equals auth.PuzzleID
                           where state.Team == team &&
                           auth.Author == author
                           select state);
                }
                else
                {
                    return(context.PuzzleStatePerTeam.Where(state => state.Team == team));
                }
            }

            return(context.PuzzleStatePerTeam.Where(state => state.Puzzle.Event == eventObj));
        }