From b9c85bcef9786217e5e681f7ce9e9b593e6ba858 Mon Sep 17 00:00:00 2001 From: Jessica Hoang Date: Wed, 22 Jan 2025 18:57:56 -0800 Subject: [PATCH 1/8] Make embeded puzzles default to true --- Data/PuzzleServerContext.cs | 1 + ServerCore/Pages/Events/Create.cshtml.cs | 1 + ServerCore/Pages/Events/CreateDemo.cshtml.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/Data/PuzzleServerContext.cs b/Data/PuzzleServerContext.cs index bbd94723..687c7aa8 100644 --- a/Data/PuzzleServerContext.cs +++ b/Data/PuzzleServerContext.cs @@ -71,6 +71,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().HasKey(state => new { state.PlayerID, state.HintID }); modelBuilder.Entity().HasIndex(eventObj => new { eventObj.UrlString }).IsUnique(); modelBuilder.Entity().Property(eventObj => eventObj.AllowBlazor).HasDefaultValue(true); + modelBuilder.Entity().Property(eventObj => eventObj.EmbedPuzzles).HasDefaultValue(true); modelBuilder.Entity().HasKey(state => new { state.PuzzleID, state.TeamID, state.Key }); modelBuilder.Entity().HasIndex(piece => new { piece.ProgressLevel }); modelBuilder.Entity().HasIndex(submission => new { submission.TeamID, submission.PuzzleID, submission.SubmissionText }).IsUnique(); diff --git a/ServerCore/Pages/Events/Create.cshtml.cs b/ServerCore/Pages/Events/Create.cshtml.cs index a3392072..110e3da1 100644 --- a/ServerCore/Pages/Events/Create.cshtml.cs +++ b/ServerCore/Pages/Events/Create.cshtml.cs @@ -48,6 +48,7 @@ public IActionResult OnGet() Event.MaxExternalsPerTeam = 9; Event.MaxTeamSize = 12; Event.AllowBlazor = true; + Event.EmbedPuzzles = true; return Page(); } diff --git a/ServerCore/Pages/Events/CreateDemo.cshtml.cs b/ServerCore/Pages/Events/CreateDemo.cshtml.cs index db4b32b0..15395850 100644 --- a/ServerCore/Pages/Events/CreateDemo.cshtml.cs +++ b/ServerCore/Pages/Events/CreateDemo.cshtml.cs @@ -100,6 +100,7 @@ public async Task OnPostAsync() Event.MaxExternalsPerTeam = 9; Event.MaxTeamSize = 12; Event.AllowBlazor = true; + Event.EmbedPuzzles = true; _context.Events.Add(Event); await _context.SaveChangesAsync(); From 72361c16d9e3dbb90c054dfec628c03565b762f9 Mon Sep 17 00:00:00 2001 From: Jessica Hoang Date: Wed, 22 Jan 2025 20:34:01 -0800 Subject: [PATCH 2/8] add migration --- ...0123043349_defaultEmbeddedTrue.Designer.cs | 2114 +++++++++++++++++ .../20250123043349_defaultEmbeddedTrue.cs | 36 + .../PuzzleServerContextModelSnapshot.cs | 4 +- 3 files changed, 2153 insertions(+), 1 deletion(-) create mode 100644 Data/Migrations/20250123043349_defaultEmbeddedTrue.Designer.cs create mode 100644 Data/Migrations/20250123043349_defaultEmbeddedTrue.cs diff --git a/Data/Migrations/20250123043349_defaultEmbeddedTrue.Designer.cs b/Data/Migrations/20250123043349_defaultEmbeddedTrue.Designer.cs new file mode 100644 index 00000000..b7994fe5 --- /dev/null +++ b/Data/Migrations/20250123043349_defaultEmbeddedTrue.Designer.cs @@ -0,0 +1,2114 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ServerCore.DataModel; + +#nullable disable + +namespace Data.Migrations +{ + [DbContext(typeof(PuzzleServerContext))] + [Migration("20250123043349_defaultEmbeddedTrue")] + partial class defaultEmbeddedTrue + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.8") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("ServerCore.DataModel.Annotation", b => + { + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("TeamID") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("int"); + + b.Property("Contents") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("PuzzleID", "TeamID", "Key"); + + b.HasIndex("TeamID"); + + b.ToTable("Annotations"); + }); + + modelBuilder.Entity("ServerCore.DataModel.ContentFile", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("EventID") + .HasColumnType("int"); + + b.Property("FileType") + .HasColumnType("int"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("ShortName") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("UrlString") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("EventID", "ShortName") + .IsUnique(); + + b.ToTable("ContentFiles"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Event", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AllowBlazor") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("AllowFeedback") + .HasColumnType("bit"); + + b.Property("AllowsRemote") + .HasColumnType("bit"); + + b.Property("Announcement") + .HasColumnType("nvarchar(max)"); + + b.Property("AnswerSubmissionEnd") + .HasColumnType("datetime2"); + + b.Property("AnswersAvailableBegin") + .HasColumnType("datetime2"); + + b.Property("ContactEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("Copyright") + .HasColumnType("nvarchar(max)"); + + b.Property("DefaultCostForHelpThread") + .HasColumnType("int"); + + b.Property("DefaultLunch") + .HasColumnType("nvarchar(max)"); + + b.Property("EmbedPuzzles") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("EphemeralHacks") + .HasColumnType("nvarchar(max)"); + + b.Property("EventBegin") + .HasColumnType("datetime2"); + + b.Property("EventHasTeamSwag") + .HasColumnType("bit"); + + b.Property("FAQContent") + .HasColumnType("nvarchar(max)"); + + b.Property("HasIndividualLunch") + .HasColumnType("bit"); + + b.Property("HasSwag") + .HasColumnType("bit") + .HasColumnName("EventHasSwag"); + + b.Property("HasTShirts") + .HasColumnType("bit"); + + b.Property("HideHints") + .HasColumnType("bit"); + + b.Property("HomeContent") + .HasColumnType("nvarchar(max)"); + + b.Property("HomePartial") + .HasColumnType("nvarchar(max)"); + + b.Property("IsAlphaTestingEvent") + .HasColumnType("bit"); + + b.Property("IsInternEvent") + .HasColumnType("bit"); + + b.Property("IsRemote") + .HasColumnType("bit"); + + b.Property("LockoutDurationMultiplier") + .HasColumnType("float"); + + b.Property("LockoutIncorrectGuessLimit") + .HasColumnType("int"); + + b.Property("LockoutIncorrectGuessPeriod") + .HasColumnType("float"); + + b.Property("LunchDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("LunchOptions") + .HasColumnType("nvarchar(max)"); + + b.Property("LunchReportDate") + .HasColumnType("datetime2"); + + b.Property("MaxExternalsPerTeam") + .HasColumnType("int"); + + b.Property("MaxNumberOfTeams") + .HasColumnType("int"); + + b.Property("MaxSubmissionCount") + .HasColumnType("bigint"); + + b.Property("MaxTeamSize") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PlayersPerLunch") + .HasColumnType("int"); + + b.Property("RulesContent") + .HasColumnType("nvarchar(max)"); + + b.Property("ShouldShowHelpMessageOnlyToAuthor") + .HasColumnType("bit"); + + b.Property("ShowFastestSolves") + .HasColumnType("bit"); + + b.Property("SinglePlayerPuzzleTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("StandingsAvailableBegin") + .HasColumnType("datetime2"); + + b.Property("StandingsOverride") + .HasColumnType("bit"); + + b.Property("TeamAnnouncement") + .HasColumnType("nvarchar(max)"); + + b.Property("TeamDeleteEnd") + .HasColumnType("datetime2"); + + b.Property("TeamMembershipChangeEnd") + .HasColumnType("datetime2"); + + b.Property("TeamMiscDataChangeEnd") + .HasColumnType("datetime2"); + + b.Property("TeamNameChangeEnd") + .HasColumnType("datetime2"); + + b.Property("TeamRegistrationBegin") + .HasColumnType("datetime2"); + + b.Property("TeamRegistrationEnd") + .HasColumnType("datetime2"); + + b.Property("TermForGroup") + .HasColumnType("nvarchar(max)"); + + b.Property("UrlString") + .HasColumnType("nvarchar(450)"); + + b.HasKey("ID"); + + b.HasIndex("UrlString") + .IsUnique() + .HasFilter("[UrlString] IS NOT NULL"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAdmins", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AdminID") + .HasColumnType("int"); + + b.Property("EventID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("AdminID"); + + b.HasIndex("EventID"); + + b.ToTable("EventAdmins"); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAuthors", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AuthorID") + .HasColumnType("int"); + + b.Property("EventID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("AuthorID"); + + b.HasIndex("EventID"); + + b.ToTable("EventAuthors"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Feedback", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("Difficulty") + .HasColumnType("int"); + + b.Property("Fun") + .HasColumnType("int"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("SubmissionTime") + .HasColumnType("datetime2"); + + b.Property("SubmitterID") + .HasColumnType("int"); + + b.Property("WrittenFeedback") + .HasColumnType("nvarchar(max)"); + + b.HasKey("ID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("SubmitterID"); + + b.ToTable("Feedback"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Hint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Cost") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Hints"); + }); + + modelBuilder.Entity("ServerCore.DataModel.HintStatePerTeam", b => + { + b.Property("TeamID") + .HasColumnType("int"); + + b.Property("HintID") + .HasColumnType("int"); + + b.Property("UnlockTime") + .HasColumnType("datetime2"); + + b.HasKey("TeamID", "HintID"); + + b.HasIndex("HintID"); + + b.ToTable("HintStatePerTeam"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Invitation", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("EmailAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("InvitationCode") + .HasColumnType("uniqueidentifier"); + + b.Property("InvitationType") + .HasColumnType("nvarchar(max)"); + + b.Property("TeamID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("TeamID"); + + b.ToTable("Invitations"); + }); + + modelBuilder.Entity("ServerCore.DataModel.LiveEvent", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AssociatedPuzzleId") + .HasColumnType("int"); + + b.Property("ClosingReminderOffset") + .HasColumnType("time"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("EventEndTimeUtc") + .HasColumnType("datetime2"); + + b.Property("EventIsScheduled") + .HasColumnType("bit"); + + b.Property("EventStartTimeUtc") + .HasColumnType("datetime2"); + + b.Property("FirstReminderOffset") + .HasColumnType("time"); + + b.Property("LastNotifiedAllTeamsUtc") + .HasColumnType("datetime2"); + + b.Property("LastReminderOffset") + .HasColumnType("time"); + + b.Property("Location") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NumberOfInstances") + .HasColumnType("int"); + + b.Property("OpeningReminderOffset") + .HasColumnType("time"); + + b.Property("TeamsPerSlot") + .HasColumnType("int"); + + b.Property("TimePerSlot") + .HasColumnType("time"); + + b.HasKey("ID"); + + b.HasIndex("AssociatedPuzzleId"); + + b.ToTable("LiveEvents"); + }); + + modelBuilder.Entity("ServerCore.DataModel.LiveEventSchedule", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("LastNotifiedUtc") + .HasColumnType("datetime2"); + + b.Property("LiveEventId") + .HasColumnType("int"); + + b.Property("StartTimeUtc") + .HasColumnType("datetime2"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("LiveEventId"); + + b.HasIndex("TeamId"); + + b.ToTable("LiveEventsSchedule"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Message", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("ClaimerID") + .HasColumnType("int"); + + b.Property("CreatedDateTimeInUtc") + .HasColumnType("datetime2"); + + b.Property("EventID") + .HasColumnType("int"); + + b.Property("IsFromGameControl") + .HasColumnType("bit"); + + b.Property("ModifiedDateTimeInUtc") + .HasColumnType("datetime2"); + + b.Property("PlayerID") + .HasColumnType("int"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("SenderID") + .HasColumnType("int"); + + b.Property("Subject") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TeamID") + .HasColumnType("int"); + + b.Property("Text") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("ThreadId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("ID"); + + b.HasIndex("ClaimerID"); + + b.HasIndex("EventID"); + + b.HasIndex("PlayerID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("SenderID"); + + b.HasIndex("TeamID"); + + b.HasIndex("ThreadId"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Piece", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("Contents") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProgressLevel") + .HasColumnType("int"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("ProgressLevel"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Pieces"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PlayerInEvent", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("EventId") + .HasColumnType("int"); + + b.Property("HintCoinCount") + .HasColumnType("int"); + + b.Property("HintCoinsUsed") + .HasColumnType("int"); + + b.Property("IsRemote") + .HasColumnType("bit"); + + b.Property("Lunch") + .HasColumnType("nvarchar(max)"); + + b.Property("LunchModifications") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("PlayerId") + .HasColumnType("int"); + + b.Property("ShirtSize") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("ID"); + + b.HasIndex("EventId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Swag"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Prerequisites", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("PrerequisiteID") + .HasColumnType("int"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("PrerequisiteID"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Prerequisites"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Puzzle", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AlphaTestsNeeded") + .HasColumnType("int"); + + b.Property("CostForHelpThread") + .HasColumnType("int"); + + b.Property("CustomAuthorText") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomCSSFile") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomSolutionURL") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomURL") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Errata") + .HasColumnType("nvarchar(max)"); + + b.Property("EventID") + .HasColumnType("int"); + + b.Property("Group") + .HasColumnType("nvarchar(max)"); + + b.Property("HasDataConfirmation") + .HasColumnType("bit"); + + b.Property("HintCoinsForSolve") + .HasColumnType("int"); + + b.Property("HintsAreCumulative") + .HasColumnType("bit"); + + b.Property("IsCheatCode") + .HasColumnType("bit"); + + b.Property("IsFinalPuzzle") + .HasColumnType("bit"); + + b.Property("IsForSinglePlayer") + .HasColumnType("bit"); + + b.Property("IsFreeform") + .HasColumnType("bit"); + + b.Property("IsGloballyVisiblePrerequisite") + .HasColumnType("bit"); + + b.Property("IsMetaPuzzle") + .HasColumnType("bit"); + + b.Property("IsPuzzle") + .HasColumnType("bit"); + + b.Property("MaxAnnotationKey") + .HasColumnType("int"); + + b.Property("MinInGroupCount") + .HasColumnType("int"); + + b.Property("MinPrerequisiteCount") + .HasColumnType("int"); + + b.Property("MinutesOfEventLockout") + .HasColumnType("int"); + + b.Property("MinutesToAutomaticallySolve") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OrderInGroup") + .HasColumnType("int"); + + b.Property("PieceMetaTagFilter") + .HasColumnType("nvarchar(max)"); + + b.Property("PieceMetaUsage") + .HasColumnType("int"); + + b.Property("PieceTag") + .HasColumnType("nvarchar(max)"); + + b.Property("PieceWeight") + .HasColumnType("int"); + + b.Property("PrerequisiteWeight") + .HasColumnType("int"); + + b.Property("PuzzleVersion") + .HasColumnType("int"); + + b.Property("ShowHelpThreadsToAllGameControl") + .HasColumnType("bit"); + + b.Property("SolveValue") + .HasColumnType("int"); + + b.Property("SupportEmailAlias") + .HasColumnType("nvarchar(max)"); + + b.Property("Token") + .HasColumnType("nvarchar(max)"); + + b.HasKey("ID"); + + b.HasIndex("EventID"); + + b.ToTable("Puzzles"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleAuthors", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AuthorID") + .HasColumnType("int"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("SupportOnly") + .HasColumnType("bit"); + + b.HasKey("ID"); + + b.HasIndex("AuthorID"); + + b.HasIndex("PuzzleID"); + + b.ToTable("PuzzleAuthors"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleStatePerTeam", b => + { + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("TeamID") + .HasColumnType("int"); + + b.Property("IsEmailOnlyMode") + .HasColumnType("bit"); + + b.Property("IsHelpThreadUnlockedByCoins") + .HasColumnType("bit"); + + b.Property("LockoutExpiryTime") + .HasColumnType("datetime2"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("Printed") + .HasColumnType("bit"); + + b.Property("SolvedTime") + .HasColumnType("datetime2"); + + b.Property("UnlockedTime") + .HasColumnType("datetime2"); + + b.Property("WrongSubmissionCountBuffer") + .HasColumnType("bigint"); + + b.HasKey("PuzzleID", "TeamID"); + + b.HasIndex("TeamID"); + + b.HasIndex("TeamID", "SolvedTime"); + + b.ToTable("PuzzleStatePerTeam"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleUser", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("Email") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("EmployeeAlias") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IdentityUserId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsGlobalAdmin") + .HasColumnType("bit"); + + b.Property("MayBeAdminOrAuthor") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("PhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("TShirtSize") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("VisibleToOthers") + .HasColumnType("bit"); + + b.HasKey("ID"); + + b.ToTable("PuzzleUsers"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Response", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("IsSolution") + .HasColumnType("bit"); + + b.Property("Note") + .HasColumnType("nvarchar(max)"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("ResponseText") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SubmittedText") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.HasKey("ID"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Responses"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Room", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("Building") + .HasColumnType("nvarchar(450)"); + + b.Property("Capacity") + .HasColumnType("int"); + + b.Property("CurrentlyOnline") + .HasColumnType("bit"); + + b.Property("EventID") + .HasColumnType("int"); + + b.Property("Group") + .HasColumnType("nvarchar(max)"); + + b.Property("Number") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("TeamID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("TeamID"); + + b.HasIndex("EventID", "Building", "Number") + .IsUnique() + .HasFilter("[Building] IS NOT NULL"); + + b.ToTable("Rooms"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleHintStatePerPlayer", b => + { + b.Property("PlayerID") + .HasColumnType("int"); + + b.Property("HintID") + .HasColumnType("int"); + + b.Property("UnlockTime") + .HasColumnType("datetime2"); + + b.HasKey("PlayerID", "HintID"); + + b.HasIndex("HintID"); + + b.ToTable("SinglePlayerPuzzleHintStatePerPlayer"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleStatePerPlayer", b => + { + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("PlayerID") + .HasColumnType("int"); + + b.Property("IsEmailOnlyMode") + .HasColumnType("bit"); + + b.Property("IsHelpThreadUnlockedByCoins") + .HasColumnType("bit"); + + b.Property("LockoutExpiryTime") + .HasColumnType("datetime2"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("Printed") + .HasColumnType("bit"); + + b.Property("SolvedTime") + .HasColumnType("datetime2"); + + b.Property("UnlockedTime") + .HasColumnType("datetime2"); + + b.Property("WrongSubmissionCountBuffer") + .HasColumnType("bigint"); + + b.HasKey("PuzzleID", "PlayerID"); + + b.HasIndex("PlayerID"); + + b.HasIndex("PlayerID", "SolvedTime"); + + b.ToTable("SinglePlayerPuzzleStatePerPlayer"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleSubmission", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AllowFreeformSharing") + .HasColumnType("bit"); + + b.Property("FreeformAccepted") + .HasColumnType("bit"); + + b.Property("FreeformFavorited") + .HasColumnType("bit"); + + b.Property("FreeformJudgeID") + .HasColumnType("int"); + + b.Property("FreeformResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("ResponseID") + .HasColumnType("int"); + + b.Property("SubmissionText") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("SubmitterID") + .HasColumnType("int"); + + b.Property("TimeSubmitted") + .HasColumnType("datetime2"); + + b.HasKey("ID"); + + b.HasIndex("FreeformJudgeID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("ResponseID"); + + b.HasIndex("SubmitterID", "PuzzleID", "SubmissionText") + .IsUnique(); + + b.ToTable("SinglePlayerPuzzleSubmissions"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleUnlockState", b => + { + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("UnlockedTime") + .HasColumnType("datetime2"); + + b.HasKey("PuzzleID"); + + b.ToTable("SinglePlayerPuzzleUnlockStates"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Submission", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AllowFreeformSharing") + .HasColumnType("bit"); + + b.Property("FreeformAccepted") + .HasColumnType("bit"); + + b.Property("FreeformFavorited") + .HasColumnType("bit"); + + b.Property("FreeformJudgeID") + .HasColumnType("int"); + + b.Property("FreeformResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("PuzzleID") + .HasColumnType("int"); + + b.Property("ResponseID") + .HasColumnType("int"); + + b.Property("SubmissionText") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("SubmitterID") + .HasColumnType("int"); + + b.Property("TeamID") + .HasColumnType("int"); + + b.Property("TeamID1") + .HasColumnType("int"); + + b.Property("TimeSubmitted") + .HasColumnType("datetime2"); + + b.HasKey("ID"); + + b.HasIndex("FreeformJudgeID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("ResponseID"); + + b.HasIndex("SubmitterID"); + + b.HasIndex("TeamID1"); + + b.HasIndex("TeamID", "PuzzleID", "SubmissionText") + .IsUnique(); + + b.ToTable("Submissions"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Team", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("AutoApproveTeammates") + .HasColumnType("bit"); + + b.Property("AutoTeamType") + .HasColumnType("int"); + + b.Property("Bio") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CustomRoom") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("EventID") + .HasColumnType("int"); + + b.Property("HintCoinCount") + .HasColumnType("int"); + + b.Property("HintCoinsUsed") + .HasColumnType("int"); + + b.Property("IsDisqualified") + .HasColumnType("bit"); + + b.Property("IsLookingForTeammates") + .HasColumnType("bit"); + + b.Property("MergedTeams") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Password") + .HasColumnType("nvarchar(max)"); + + b.Property("PrimaryContactEmail") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("PrimaryPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("SecondaryPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("ShowTeamAnnouncement") + .HasColumnType("bit"); + + b.HasKey("ID"); + + b.HasIndex("EventID"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamApplication", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("PlayerID") + .HasColumnType("int"); + + b.Property("TeamID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("PlayerID"); + + b.HasIndex("TeamID"); + + b.ToTable("TeamApplications"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamLunch", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("Lunch") + .HasColumnType("nvarchar(max)"); + + b.Property("LunchModifications") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamLunch"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamMembers", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("Team.ID") + .HasColumnType("int"); + + b.Property("User.ID") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("Team.ID"); + + b.HasIndex("User.ID"); + + b.ToTable("TeamMembers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ServerCore.DataModel.Annotation", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Puzzle"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.ContentFile", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany("Contents") + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAdmins", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Admin") + .WithMany() + .HasForeignKey("AdminID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Admin"); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAuthors", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Author") + .WithMany() + .HasForeignKey("AuthorID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Author"); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Feedback", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Submitter") + .WithMany() + .HasForeignKey("SubmitterID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Puzzle"); + + b.Navigation("Submitter"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Hint", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.HintStatePerTeam", b => + { + b.HasOne("ServerCore.DataModel.Hint", "Hint") + .WithMany() + .HasForeignKey("HintID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Hint"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Invitation", b => + { + b.HasOne("ServerCore.DataModel.Team", null) + .WithMany("Invitations") + .HasForeignKey("TeamID"); + }); + + modelBuilder.Entity("ServerCore.DataModel.LiveEvent", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "AssociatedPuzzle") + .WithMany() + .HasForeignKey("AssociatedPuzzleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AssociatedPuzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.LiveEventSchedule", b => + { + b.HasOne("ServerCore.DataModel.LiveEvent", "LiveEvent") + .WithMany() + .HasForeignKey("LiveEventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("LiveEvent"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Message", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Claimer") + .WithMany() + .HasForeignKey("ClaimerID") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Player") + .WithMany() + .HasForeignKey("PlayerID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Sender") + .WithMany() + .HasForeignKey("SenderID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Claimer"); + + b.Navigation("Event"); + + b.Navigation("Player"); + + b.Navigation("Puzzle"); + + b.Navigation("Sender"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Piece", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PlayerInEvent", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Prerequisites", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Prerequisite") + .WithMany() + .HasForeignKey("PrerequisiteID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Prerequisite"); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Puzzle", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleAuthors", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Author") + .WithMany() + .HasForeignKey("AuthorID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Author"); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleStatePerTeam", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Puzzle"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Response", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Room", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID"); + + b.Navigation("Event"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleHintStatePerPlayer", b => + { + b.HasOne("ServerCore.DataModel.Hint", "Hint") + .WithMany() + .HasForeignKey("HintID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Player") + .WithMany() + .HasForeignKey("PlayerID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Hint"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleStatePerPlayer", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Player") + .WithMany() + .HasForeignKey("PlayerID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleSubmission", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "FreeformJudge") + .WithMany() + .HasForeignKey("FreeformJudgeID"); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany("SinglePlayerPuzzleSubmissions") + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Response", "Response") + .WithMany() + .HasForeignKey("ResponseID"); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Submitter") + .WithMany() + .HasForeignKey("SubmitterID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FreeformJudge"); + + b.Navigation("Puzzle"); + + b.Navigation("Response"); + + b.Navigation("Submitter"); + }); + + modelBuilder.Entity("ServerCore.DataModel.SinglePlayerPuzzleUnlockState", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Puzzle"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Submission", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "FreeformJudge") + .WithMany() + .HasForeignKey("FreeformJudgeID"); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany("Submissions") + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Response", "Response") + .WithMany() + .HasForeignKey("ResponseID"); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Submitter") + .WithMany() + .HasForeignKey("SubmitterID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", null) + .WithMany("Submissions") + .HasForeignKey("TeamID1"); + + b.Navigation("FreeformJudge"); + + b.Navigation("Puzzle"); + + b.Navigation("Response"); + + b.Navigation("Submitter"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Team", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamApplication", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Player") + .WithMany() + .HasForeignKey("PlayerID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamLunch", b => + { + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamMembers", b => + { + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("Team.ID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Member") + .WithMany() + .HasForeignKey("User.ID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Member"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Puzzle", b => + { + b.Navigation("Contents"); + + b.Navigation("SinglePlayerPuzzleSubmissions"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Team", b => + { + b.Navigation("Invitations"); + + b.Navigation("Submissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/20250123043349_defaultEmbeddedTrue.cs b/Data/Migrations/20250123043349_defaultEmbeddedTrue.cs new file mode 100644 index 00000000..40addfba --- /dev/null +++ b/Data/Migrations/20250123043349_defaultEmbeddedTrue.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Data.Migrations +{ + /// + public partial class defaultEmbeddedTrue : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "EmbedPuzzles", + table: "Events", + type: "bit", + nullable: false, + defaultValue: true, + oldClrType: typeof(bool), + oldType: "bit"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "EmbedPuzzles", + table: "Events", + type: "bit", + nullable: false, + oldClrType: typeof(bool), + oldType: "bit", + oldDefaultValue: true); + } + } +} diff --git a/Data/Migrations/PuzzleServerContextModelSnapshot.cs b/Data/Migrations/PuzzleServerContextModelSnapshot.cs index 7186581e..fb6b0d5f 100644 --- a/Data/Migrations/PuzzleServerContextModelSnapshot.cs +++ b/Data/Migrations/PuzzleServerContextModelSnapshot.cs @@ -328,7 +328,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("EmbedPuzzles") - .HasColumnType("bit"); + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); b.Property("EphemeralHacks") .HasColumnType("nvarchar(max)"); From 4ae544b368921865ddd6aab51e1e7053fca5352f Mon Sep 17 00:00:00 2001 From: Jessica Hoang Date: Mon, 10 Feb 2025 21:31:14 -0800 Subject: [PATCH 3/8] Add single player puzzle state map --- .../Events/SinglePlayerPuzzleStateMap.cshtml | 67 ++++++++ .../SinglePlayerPuzzleStateMap.cshtml.cs | 159 ++++++++++++++++++ .../Shared/_EventNavigationPartial.cshtml | 1 + 3 files changed, 227 insertions(+) create mode 100644 ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml create mode 100644 ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs diff --git a/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml new file mode 100644 index 00000000..bf1843f9 --- /dev/null +++ b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml @@ -0,0 +1,67 @@ +@page "/{eventId}/{eventRole}/Events/SinglePlayerPuzzleStateMap" +@model ServerCore.Pages.Events.SinglePlayerPuzzleStateMapModel + +@{ + @using Helpers; + + ViewData["Title"] = "SinglePlayerPuzzle State Map"; + ViewData["AdminRoute"] = "/Events/SinglePlayerPuzzleStateMap"; + ViewData["AuthorRoute"] = "/Events/SinglePlayerPuzzleStateMap"; +} + +

Single Player Puzzle State Map

+ + + + + + + + @foreach (SinglePlayerPuzzleStateMapModel.PuzzleStats puzzleStat in Model.PuzzleStatList) + { + + } + + + + @for (int playerIndex = 0; playerIndex < Model.PlayerStatList.Count; playerIndex++) + { + var playerStat = Model.PlayerStatList[playerIndex]; + var rank = playerIndex + 1; + + + + + + @for (int puzzleIndex = 0; puzzleIndex < Model.PuzzleStatList.Count; puzzleIndex++) + { + var statState = Model.StateMap[playerIndex, puzzleIndex] ?? SinglePlayerPuzzleStateMapModel.StateStats.Default; + + + } + + } + +
+ Rank + + Name + + Puzzles + + Score + + @puzzleStat.Puzzle.PlaintextName (@puzzleStat.SolveCount) +
+ @rank + + @(playerStat.Player.Name) + + @playerStat.SolveCount + + @playerStat.Score + + + +
+ diff --git a/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs new file mode 100644 index 00000000..c55d69cb --- /dev/null +++ b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ServerCore.DataModel; +using ServerCore.Helpers; +using ServerCore.ModelBases; + +namespace ServerCore.Pages.Events +{ + [Authorize(Policy = "IsEventAdminOrEventAuthor")] + public class SinglePlayerPuzzleStateMapModel : EventSpecificPageModel + { + public List PuzzleStatList { get; private set; } + + public List PlayerStatList { get; private set; } + + public StateStats[,] StateMap { get; private set; } + + public SinglePlayerPuzzleStateMapModel(PuzzleServerContext serverContext, + UserManager userManager) + : base(serverContext, userManager) + { + } + + public async Task OnGetAsync(string? groups) + { + // Get relevant SinglePlayerPuzzles + List puzzles; + if (EventRole == EventRole.admin) + { + puzzles = await _context.Puzzles.Where(p => p.Event == Event) + .Where(p => p.IsForSinglePlayer) + .ToListAsync(); + } + else + { + puzzles = await UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) + .Where(p => p.IsForSinglePlayer) + .ToListAsync(); + } + + // Gets all relevant single player puzzle states per player + List puzzleStatesPerPlayer = await _context.SinglePlayerPuzzleStatePerPlayer + .Where(puzzleState => puzzleState.Puzzle.Event == this.Event && puzzleState.Puzzle.IsForSinglePlayer) + .ToListAsync(); + + // Create the puzzle and player stats + Dictionary puzzleIdToPuzzleStatsMap = puzzles.ToDictionary(puzzle => puzzle.ID, puzzle => new PuzzleStats() { Puzzle = puzzle}); + var playerIdToPlayerStatsMap = new Dictionary(); + foreach (SinglePlayerPuzzleStatePerPlayer puzzleState in puzzleStatesPerPlayer) + { + if (!playerIdToPlayerStatsMap.ContainsKey(puzzleState.PlayerID)) + { + playerIdToPlayerStatsMap[puzzleState.PlayerID] = new PlayerStats { Player = puzzleState.Player }; + } + + if (puzzleState.SolvedTime != null) + { + puzzleIdToPuzzleStatsMap[puzzleState.PuzzleID].SolveCount++; + playerIdToPlayerStatsMap[puzzleState.PlayerID].SolveCount++; + playerIdToPlayerStatsMap[puzzleState.PlayerID].Score += puzzleState.Puzzle.SolveValue; + } + } + + // Order both lists + this.PuzzleStatList = puzzleIdToPuzzleStatsMap + .Values + .OrderBy(puzzleStat => puzzleStat.SolveCount) + .ThenBy(puzzleStat => puzzleStat.Puzzle.Name) + .ToList(); + + this.PlayerStatList = playerIdToPlayerStatsMap + .Values + .OrderBy(playerStat => playerStat.Score) + .ThenBy(playerStat => playerStat.SolveCount) + .ThenBy(playerStat => playerStat.Player.Name) + .ToList(); + + // Create maps from each puzzle/player id to it's sorted index + var index = 0; + Dictionary puzzleIdToIndexMap = this.PuzzleStatList.ToDictionary(puzzleStat => puzzleStat.Puzzle.ID, puzzleStat => index++); + + index = 0; + Dictionary playerIdToIndexMap = this.PlayerStatList.ToDictionary(playerStat => playerStat.Player.ID, playerStat => index++); + + // Create final stat matrix + this.StateMap = new StateStats[this.PlayerStatList.Count, this.PuzzleStatList.Count]; + foreach (SinglePlayerPuzzleStatePerPlayer statePerPlayer in puzzleStatesPerPlayer) + { + int playerIndex = playerIdToIndexMap[statePerPlayer.PlayerID]; + int puzzleIndex = puzzleIdToIndexMap[statePerPlayer.Puzzle.ID]; + this.StateMap[playerIndex, puzzleIndex] = new StateStats() + { + UnlockedTime = statePerPlayer.UnlockedTime, + SolvedTime = statePerPlayer.SolvedTime, + LockedOut = statePerPlayer.IsLockedOut + }; + } + + return Page(); + } + + public class PuzzleStats + { + public Puzzle Puzzle { get; set; } + public int SolveCount { get; set; } + } + + public class PlayerStats + { + public PuzzleUser Player { get; set; } + public int SolveCount { get; set; } + public int Score { get; set; } + } + + public class StateStats + { + public static StateStats Default => new StateStats(); + + public DateTime? UnlockedTime { get; set; } + public DateTime? SolvedTime { get; set; } + public bool LockedOut { get; set; } + + public string Classes + { + get + { + string puzzleState = null; + + if (LockedOut) + { + puzzleState = "email-mode"; + } + else if (SolvedTime != null) + { + int minutes = (int)((DateTime.UtcNow - SolvedTime.Value).TotalMinutes); + puzzleState = minutes > 15 ? "solved-old" : "solved-recent"; + } + else if (UnlockedTime == null) + { + puzzleState = "still-locked"; + } + else + { + int minutes = (int)((DateTime.UtcNow - UnlockedTime.Value).TotalMinutes); + puzzleState = minutes > 15 ? "unlocked-old" : "unlocked-recent"; + } + + return puzzleState != null ? $"statecell {puzzleState}" : "statecell"; + } + } + } + } +} diff --git a/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml b/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml index db1b4210..e7c0e56e 100644 --- a/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml +++ b/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml @@ -67,6 +67,7 @@
  • Standings
  • Fastest solves
  • Puzzle state map
  • +
  • Single player puzzle state map
  • Shared submissions
  • From d5406370fea70f939a504f44893ae9ddc9636bc0 Mon Sep 17 00:00:00 2001 From: Jessica Hoang Date: Mon, 10 Feb 2025 21:44:25 -0800 Subject: [PATCH 4/8] don't show dropdown when single player puzzle not available --- ServerCore/Pages/Shared/_EventNavigationPartial.cshtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml b/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml index e7c0e56e..f85f9b13 100644 --- a/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml +++ b/ServerCore/Pages/Shared/_EventNavigationPartial.cshtml @@ -67,7 +67,10 @@
  • Standings
  • Fastest solves
  • Puzzle state map
  • -
  • Single player puzzle state map
  • + @if (Event.ShouldShowSinglePlayerPuzzles) + { +
  • Single player puzzle state map
  • + }
  • Shared submissions
  • From 2ee456b27a2720df12ff875fd00fb48de4658d1b Mon Sep 17 00:00:00 2001 From: Jessica Hoang Date: Sun, 16 Feb 2025 18:02:49 -0800 Subject: [PATCH 5/8] Share code --- ServerCore/Pages/Events/Map.cshtml.cs | 15 ++++--- .../Events/SinglePlayerPuzzleStateMap.cshtml | 2 +- .../SinglePlayerPuzzleStateMap.cshtml.cs | 45 +++---------------- 3 files changed, 16 insertions(+), 46 deletions(-) diff --git a/ServerCore/Pages/Events/Map.cshtml.cs b/ServerCore/Pages/Events/Map.cshtml.cs index d1f6bd9d..79834d49 100644 --- a/ServerCore/Pages/Events/Map.cshtml.cs +++ b/ServerCore/Pages/Events/Map.cshtml.cs @@ -245,17 +245,13 @@ public class TeamStats public DateTime FinalMetaSolveTime { get; set; } = DateTime.MaxValue; } - public class StateStats + public abstract class StateStatsBase { - public static StateStats Default { get; } = new StateStats(); - public PuzzleStats Puzzle { get; set; } - public TeamStats Team { get; set; } - public bool UnlockedAtStart { get; set; } public DateTime? UnlockedTime { get; set; } + public bool UnlockedAtStart { get; set; } public DateTime? SolvedTime { get; set; } public bool LockedOut { get; set; } - public bool IsPresent { get; set; } public string Classes { @@ -299,5 +295,12 @@ public string Classes } } } + + public class StateStats : StateStatsBase + { + public static StateStats Default { get; } = new StateStats(); + public TeamStats Team { get; set; } + public bool IsPresent { get; set; } + } } } diff --git a/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml index bf1843f9..d38420b4 100644 --- a/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml +++ b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml @@ -25,7 +25,7 @@ Score - @foreach (SinglePlayerPuzzleStateMapModel.PuzzleStats puzzleStat in Model.PuzzleStatList) + @foreach (MapModel.PuzzleStats puzzleStat in Model.PuzzleStatList) { @puzzleStat.Puzzle.PlaintextName (@puzzleStat.SolveCount) diff --git a/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs index c55d69cb..de755dc6 100644 --- a/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs +++ b/ServerCore/Pages/Events/SinglePlayerPuzzleStateMap.cshtml.cs @@ -9,6 +9,7 @@ using ServerCore.DataModel; using ServerCore.Helpers; using ServerCore.ModelBases; +using static ServerCore.Pages.Events.MapModel; namespace ServerCore.Pages.Events { @@ -105,12 +106,6 @@ public async Task OnGetAsync(string? groups) return Page(); } - public class PuzzleStats - { - public Puzzle Puzzle { get; set; } - public int SolveCount { get; set; } - } - public class PlayerStats { public PuzzleUser Player { get; set; } @@ -118,42 +113,14 @@ public class PlayerStats public int Score { get; set; } } - public class StateStats + public class StateStats : StateStatsBase { - public static StateStats Default => new StateStats(); - - public DateTime? UnlockedTime { get; set; } - public DateTime? SolvedTime { get; set; } - public bool LockedOut { get; set; } - - public string Classes + public StateStats() { - get - { - string puzzleState = null; - - if (LockedOut) - { - puzzleState = "email-mode"; - } - else if (SolvedTime != null) - { - int minutes = (int)((DateTime.UtcNow - SolvedTime.Value).TotalMinutes); - puzzleState = minutes > 15 ? "solved-old" : "solved-recent"; - } - else if (UnlockedTime == null) - { - puzzleState = "still-locked"; - } - else - { - int minutes = (int)((DateTime.UtcNow - UnlockedTime.Value).TotalMinutes); - puzzleState = minutes > 15 ? "unlocked-old" : "unlocked-recent"; - } - - return puzzleState != null ? $"statecell {puzzleState}" : "statecell"; - } + this.UnlockedAtStart = false; } + + public static StateStats Default => new StateStats(); } } } From fc542c2927eeabfe34fd8a3827dcea7eb877003b Mon Sep 17 00:00:00 2001 From: Jessica Date: Sat, 29 Nov 2025 01:06:37 -0800 Subject: [PATCH 6/8] Remove references to blazor --- ServerCore/Pages/Puzzles/Play.cshtml | 4 ++-- ServerCore/Pages/Shared/_EventNavigationPartial.cshtml | 10 +++++----- ServerCore/Pages/Shared/_Layout.cshtml | 2 +- ServerCore/Pages/Submissions/Index.cshtml | 2 +- ServerCore/Pages/Teams/Delete.cshtml.cs | 2 +- ServerCore/Pages/Teams/List.cshtml | 10 +--------- ServerCore/Pages/Teams/Signup.cshtml.cs | 5 ----- 7 files changed, 11 insertions(+), 24 deletions(-) diff --git a/ServerCore/Pages/Puzzles/Play.cshtml b/ServerCore/Pages/Puzzles/Play.cshtml index c3106874..5c4ecd06 100644 --- a/ServerCore/Pages/Puzzles/Play.cshtml +++ b/ServerCore/Pages/Puzzles/Play.cshtml @@ -26,7 +26,7 @@ } -@if (Model.Event.AllowBlazor && canUsePresence && Model.Team != null) +@if (canUsePresence && Model.Team != null) {