diff --git a/Dockerfile b/Dockerfile index e9c42f53..3ea1117d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,12 @@ # # multi-stage target: dev # -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS dev +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS dev ENV ASPNETCORE_URLS=http://*:5000 \ ASPNETCORE_ENVIRONMENT=DEVELOPMENT +WORKDIR /app COPY . /app WORKDIR /app/src/Gameboard.Api @@ -15,18 +16,21 @@ CMD ["dotnet", "run"] # # multi-stage target: prod # -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS prod +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS prod ARG commit ENV COMMIT=$commit # install tools for PNG generation on the server -RUN apt-get update && apt-get install -y wget && apt-get clean -RUN wget -O ~/wkhtmltopdf.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb -RUN apt-get install -y ~/wkhtmltopdf.deb -RUN rm ~/wkhtmltopdf.deb +RUN apt-get update \ + && apt-get install -y --no-install-recommends wkhtmltopdf \ + && rm -rf /var/lib/apt/lists/* + +# sanity check so CI fails early if package layout changes +RUN which wkhtmltoimage && wkhtmltoimage --version COPY --from=dev /app/dist /app COPY --from=dev /app/LICENSE.md /app/LICENSE.md + WORKDIR /app EXPOSE 80 ENV ASPNETCORE_URLS=http://*:80 diff --git a/src/Gameboard.Api.Tests.Integration/Gameboard.Api.Tests.Integration.csproj b/src/Gameboard.Api.Tests.Integration/Gameboard.Api.Tests.Integration.csproj index 1f3712a5..2ae9f8cf 100644 --- a/src/Gameboard.Api.Tests.Integration/Gameboard.Api.Tests.Integration.csproj +++ b/src/Gameboard.Api.Tests.Integration/Gameboard.Api.Tests.Integration.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable Gameboard.Api.Tests.Integration @@ -11,27 +11,27 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Gameboard.Api.Tests.Shared/Gameboard.Api.Tests.Shared.csproj b/src/Gameboard.Api.Tests.Shared/Gameboard.Api.Tests.Shared.csproj index fd2b1ae7..32bcadb2 100644 --- a/src/Gameboard.Api.Tests.Shared/Gameboard.Api.Tests.Shared.csproj +++ b/src/Gameboard.Api.Tests.Shared/Gameboard.Api.Tests.Shared.csproj @@ -1,6 +1,6 @@ - net8.0 + net10.0 enable enable Gameboard.Api.Tests.Shared @@ -9,7 +9,7 @@ - + diff --git a/src/Gameboard.Api.Tests.Unit/Gameboard.Api.Tests.Unit.csproj b/src/Gameboard.Api.Tests.Unit/Gameboard.Api.Tests.Unit.csproj index 0815467d..e868d0ef 100644 --- a/src/Gameboard.Api.Tests.Unit/Gameboard.Api.Tests.Unit.csproj +++ b/src/Gameboard.Api.Tests.Unit/Gameboard.Api.Tests.Unit.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable Gameboard.Api.Tests.Unit @@ -13,17 +13,17 @@ - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Gameboard.Api.Tests.Unit/Tests/Features/Reports/EnrollmentReportServiceTests.cs b/src/Gameboard.Api.Tests.Unit/Tests/Features/Reports/EnrollmentReportServiceTests.cs index d9cbf43f..23a8467d 100644 --- a/src/Gameboard.Api.Tests.Unit/Tests/Features/Reports/EnrollmentReportServiceTests.cs +++ b/src/Gameboard.Api.Tests.Unit/Tests/Features/Reports/EnrollmentReportServiceTests.cs @@ -12,15 +12,14 @@ public class EnrollmentReportServiceTests [Theory, GameboardAutoData] public async Task GetResults_WithOnePlayerAndChallenge_ReportsCompleteSolve(IFixture fixture) { - // given + // given var sponsors = new Data.Sponsor { Id = "good-people", Name = "The Good People", Logo = "good-people.jpg" } - .ToEnumerable() - .BuildMock(); + .ToEnumerable().ToList().BuildMock(); var challenge = fixture.Create(); challenge.Points = 50; @@ -57,7 +56,7 @@ public async Task GetResults_WithOnePlayerAndChallenge_ReportsCompleteSolve(IFix [Theory, GameboardAutoData] public async Task GetResults_WithOneTeamRecord_ReportsExpectedValues(string gameId, IFixture fixture) { - // given + // given var sponsors = new List { new() @@ -124,7 +123,7 @@ public async Task GetResults_WithOneTeamRecord_ReportsExpectedValues(string game [Theory, GameboardAutoData] public async Task GetResults_WithGameInPracAndChallengeInComp_ReportsOneResult(IFixture fixture) { - // given + // given var sponsors = new List { new() @@ -169,7 +168,7 @@ public async Task GetResults_WithGameInPracAndChallengeInComp_ReportsOneResult(I [Theory, GameboardAutoData] public async Task GetResults_WithPlayerWithNoChallenges_IsIncluded(IFixture fixture) { - // given + // given var sponsors = new List { new() @@ -184,7 +183,7 @@ public async Task GetResults_WithPlayerWithNoChallenges_IsIncluded(IFixture fixt player.Challenges = Array.Empty(); player.Game = fixture.Create(); player.Sponsor = sponsors.First(); - var players = player.ToEnumerable().BuildMock(); + var players = player.ToEnumerable().ToList().BuildMock(); var reportsService = A.Fake(); A.CallTo(() => reportsService.ParseMultiSelectCriteria(string.Empty)) diff --git a/src/Gameboard.Api/Data/Store/Store.cs b/src/Gameboard.Api/Data/Store/Store.cs index 34c0058f..e2edd60b 100644 --- a/src/Gameboard.Api/Data/Store/Store.cs +++ b/src/Gameboard.Api/Data/Store/Store.cs @@ -25,7 +25,7 @@ public interface IStore Task ExecuteUpdateAsync ( Expression> predicate, - Expression, SetPropertyCalls>> setPropertyCalls + Action> setPropertyCalls ) where TEntity : class, IEntity; Task Exists(string id) where TEntity : class, IEntity; Task FirstOrDefaultAsync(CancellationToken cancellationToken) where TEntity : class, IEntity; @@ -99,7 +99,7 @@ public async Task DoTransaction(Func operation, Cancel public Task ExecuteUpdateAsync ( Expression> predicate, - Expression, SetPropertyCalls>> setPropertyCalls + Action> setPropertyCalls ) where TEntity : class, IEntity => _dbContext .Set() diff --git a/src/Gameboard.Api/Extensions/SwaggerStartupExtensions.cs b/src/Gameboard.Api/Extensions/SwaggerStartupExtensions.cs index 4ec73d3c..f397dff7 100644 --- a/src/Gameboard.Api/Extensions/SwaggerStartupExtensions.cs +++ b/src/Gameboard.Api/Extensions/SwaggerStartupExtensions.cs @@ -7,7 +7,7 @@ using System.Reflection; using Gameboard.Api; using Microsoft.AspNetCore.Builder; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Microsoft.Extensions.DependencyInjection { @@ -63,16 +63,10 @@ OpenApiOptions openapi }, }); - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }, - new[] { oidc.Audience } - } - }); + options.AddSecurityRequirement((document) => new OpenApiSecurityRequirement + { + { new OpenApiSecuritySchemeReference("oauth2", document), [oidc.Audience] } + }); } }); diff --git a/src/Gameboard.Api/Features/Extensions/MattermostExtensionService.cs b/src/Gameboard.Api/Features/Extensions/MattermostExtensionService.cs index ce1a4862..a1b4d2c1 100644 --- a/src/Gameboard.Api/Features/Extensions/MattermostExtensionService.cs +++ b/src/Gameboard.Api/Features/Extensions/MattermostExtensionService.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; +using Duende.IdentityModel.Client; using Gameboard.Api.Data; namespace Gameboard.Api.Features.Extensions; diff --git a/src/Gameboard.Api/Features/Game/GameController.cs b/src/Gameboard.Api/Features/Game/GameController.cs index c73eaeae..d6359f80 100644 --- a/src/Gameboard.Api/Features/Game/GameController.cs +++ b/src/Gameboard.Api/Features/Game/GameController.cs @@ -134,7 +134,7 @@ public async Task> List([FromQuery] GameSearchFilter model) /// /// List games for admin interfaces. - /// + /// /// NOTE: This endpoint will eventually replace /api/games and take its path - it's just a modernized /// expression of the same utility. /// @@ -207,7 +207,8 @@ public async Task ImportGames([FromForm] ImportGamesRequest requ } [HttpPost("/api/games/import/preview")] - public async Task PreviewImportPackage([FromForm] IFormFile packageFile, CancellationToken cancellationToken) + [Consumes("multipart/form-data")] + public async Task PreviewImportPackage(IFormFile packageFile, CancellationToken cancellationToken) { var package = await packageFile.ToBytes(cancellationToken); return await mediator.Send(new PreviewImportPackageQuery(package), cancellationToken); diff --git a/src/Gameboard.Api/Features/Sponsor/SponsorController.cs b/src/Gameboard.Api/Features/Sponsor/SponsorController.cs index f90d6b84..c5c35b3f 100644 --- a/src/Gameboard.Api/Features/Sponsor/SponsorController.cs +++ b/src/Gameboard.Api/Features/Sponsor/SponsorController.cs @@ -4,14 +4,14 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Gameboard.Api.Common.Services; +using Gameboard.Api.Features.Sponsors; +using Gameboard.Api.Features.Users; +using Gameboard.Api.Services; +using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using MediatR; -using Gameboard.Api.Features.Sponsors; -using Gameboard.Api.Services; -using Gameboard.Api.Common.Services; -using Gameboard.Api.Features.Users; namespace Gameboard.Api.Controllers; @@ -95,8 +95,16 @@ public Task Update([FromBody] UpdateSponsorRequest model) [HttpPut("api/sponsor/{sponsorId}/avatar")] [Authorize] - public Task UpdateSponsorAvatar([FromRoute] string sponsorId, [FromForm] IFormFile avatarFile, CancellationToken cancellationToken) - => _mediator.Send(new SetSponsorAvatarCommand(sponsorId, avatarFile, _actingUserService.Get()), cancellationToken); + [Consumes("multipart/form-data")] + public Task UpdateSponsorAvatar( + [FromRoute] string sponsorId, + IFormFile avatarFile, // <- no [FromForm] on IFormFile + CancellationToken cancellationToken + ) + => _mediator.Send( + new SetSponsorAvatarCommand(sponsorId, avatarFile, _actingUserService.Get()), + cancellationToken + ); /// /// Delete sponsor diff --git a/src/Gameboard.Api/Gameboard.Api.csproj b/src/Gameboard.Api/Gameboard.Api.csproj index 7f2f33c4..2249ae90 100644 --- a/src/Gameboard.Api/Gameboard.Api.csproj +++ b/src/Gameboard.Api/Gameboard.Api.csproj @@ -1,14 +1,14 @@ - - - - - - - - - + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -16,17 +16,17 @@ - - - - + + + + - - - - + + + + - + @@ -35,7 +35,7 @@ - net8.0 + net10.0 diff --git a/src/Gameboard.Api/Migrations/20251202163308_dotnet10_version_update.Designer.cs b/src/Gameboard.Api/Migrations/20251202163308_dotnet10_version_update.Designer.cs new file mode 100644 index 00000000..41dbbbdc --- /dev/null +++ b/src/Gameboard.Api/Migrations/20251202163308_dotnet10_version_update.Designer.cs @@ -0,0 +1,2461 @@ +// Copyright 2025 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. + +// +using System; +using Gameboard.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Gameboard.Api.Migrations +{ + [DbContext(typeof(GameboardDbContext))] + [Migration("20251202163308_dotnet10_version_update")] + partial class dotnet10_version_update + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("GameGameExportBatch", b => + { + b.Property("ExportedInBatchesId") + .HasColumnType("text"); + + b.Property("IncludedGamesId") + .HasColumnType("character varying(40)"); + + b.HasKey("ExportedInBatchesId", "IncludedGamesId"); + + b.HasIndex("IncludedGamesId"); + + b.ToTable("GameGameExportBatch"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ApiKey", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ExpiresOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NULL"); + + b.Property("GeneratedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnerId") + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ArchivedChallenge", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Duration") + .HasColumnType("bigint"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Events") + .HasColumnType("text"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasGamespaceDeployed") + .HasColumnType("boolean"); + + b.Property("LastScoreTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSyncTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayerMode") + .HasColumnType("integer"); + + b.Property("PlayerName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Result") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("State") + .HasColumnType("text"); + + b.Property("Submissions") + .HasColumnType("text"); + + b.Property("Tag") + .HasColumnType("text"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TeamMembers") + .HasColumnType("text"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.ToTable("ArchivedChallenges"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.AwardedChallengeBonus", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeBonusId") + .HasColumnType("character varying(40)"); + + b.Property("ChallengeId") + .HasColumnType("character varying(40)"); + + b.Property("EnteredOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("InternalSummary") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeBonusId"); + + b.HasIndex("ChallengeId"); + + b.ToTable("AwardedChallengeBonuses"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.CertificateTemplate", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.ToTable("CertificateTemplate"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Challenge", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameEngineType") + .HasColumnType("integer"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GraderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasDeployedGamespace") + .HasColumnType("boolean"); + + b.Property("LastScoreTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSyncTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PendingSubmission") + .HasColumnType("text"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayerMode") + .HasColumnType("integer"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("double precision"); + + b.Property("SpecId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("State") + .HasColumnType("text"); + + b.Property("Tag") + .HasColumnType("text"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("WhenCreated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("SpecId"); + + b.HasIndex("TeamId"); + + b.ToTable("Challenges"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonus", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeBonusType") + .HasColumnType("integer"); + + b.Property("ChallengeSpecId") + .HasColumnType("character varying(40)"); + + b.Property("Description") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PointValue") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeSpecId"); + + b.ToTable("ChallengeBonuses"); + + b.HasDiscriminator("ChallengeBonusType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeEvent", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Text") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeId"); + + b.ToTable("ChallengeEvents"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeGate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("RequiredId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("RequiredScore") + .HasColumnType("double precision"); + + b.Property("TargetId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.ToTable("ChallengeGates"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSpec", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AverageDeploySeconds") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExternalId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameEngineType") + .HasColumnType("integer"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("IsHidden") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("R") + .HasColumnType("real"); + + b.Property("ShowSolutionGuideInCompetitiveMode") + .HasColumnType("boolean"); + + b.Property("SolutionGuideUrl") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Tag") + .HasColumnType("text"); + + b.Property("Tags") + .HasColumnType("text"); + + b.Property("Text") + .HasColumnType("text"); + + b.Property("TextSearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Name", "Id", "Description", "GameId", "Tag", "Tags", "Text" }); + + b.Property("X") + .HasColumnType("real"); + + b.Property("Y") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("TextSearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("TextSearchVector"), "GIN"); + + b.ToTable("ChallengeSpecs"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSubmission", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Answers") + .IsRequired() + .HasColumnType("text"); + + b.Property("ChallengeId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Score") + .ValueGeneratedOnAdd() + .HasColumnType("double precision") + .HasDefaultValue(0.0); + + b.Property("SubmittedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeId"); + + b.ToTable("ChallengeSubmissions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.DenormalizedTeamScore", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("CumulativeTimeMs") + .HasColumnType("double precision"); + + b.Property("GameId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("ScoreAdvanced") + .HasColumnType("double precision"); + + b.Property("ScoreAutoBonus") + .HasColumnType("double precision"); + + b.Property("ScoreChallenge") + .HasColumnType("double precision"); + + b.Property("ScoreManualBonus") + .HasColumnType("double precision"); + + b.Property("ScoreOverall") + .HasColumnType("double precision"); + + b.Property("SolveCountComplete") + .HasColumnType("integer"); + + b.Property("SolveCountNone") + .HasColumnType("integer"); + + b.Property("SolveCountPartial") + .HasColumnType("integer"); + + b.Property("TeamId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TeamName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.ToTable("DenormalizedTeamScores"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Extension", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("HostUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasAlternateKey("Type"); + + b.ToTable("Extensions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameHost", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ClientUrl") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DestroyResourcesOnDeployFailure") + .HasColumnType("boolean"); + + b.Property("GamespaceDeployBatchSize") + .HasColumnType("integer"); + + b.Property("HostApiKey") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("HostUrl") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("HttpTimeoutInSeconds") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PingEndpoint") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("StartupEndpoint") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("TeamExtendedEndpoint") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.ToTable("ExternalGameHosts"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameTeam", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("DeployStatus") + .HasColumnType("integer"); + + b.Property("ExternalGameUrl") + .HasColumnType("text"); + + b.Property("GameId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("TeamId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasAlternateKey("TeamId", "GameId"); + + b.HasIndex("GameId"); + + b.ToTable("ExternalGameTeams"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Feedback", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Answers") + .HasColumnType("text"); + + b.Property("ChallengeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeSpecId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Submitted") + .HasColumnType("boolean"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeId"); + + b.HasIndex("ChallengeSpecId"); + + b.HasIndex("GameId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("UserId"); + + b.ToTable("Feedback"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmission", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AttachedEntityType") + .HasColumnType("integer"); + + b.Property("FeedbackTemplateId") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("WhenCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("WhenEdited") + .HasColumnType("timestamp with time zone"); + + b.Property("WhenFinalized") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackTemplateId"); + + b.HasIndex("UserId"); + + b.ToTable("FeedbackSubmissions"); + + b.HasDiscriminator("AttachedEntityType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackTemplate", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("HelpText") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.ToTable("FeedbackTemplates"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Game", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AllowLateStart") + .HasColumnType("boolean"); + + b.Property("AllowPreview") + .HasColumnType("boolean"); + + b.Property("AllowPublicScoreboardAccess") + .HasColumnType("boolean"); + + b.Property("AllowReset") + .HasColumnType("boolean"); + + b.Property("Background") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CardText1") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CardText2") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CardText3") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CertificateTemplateId") + .HasColumnType("text"); + + b.Property("ChallengesFeedbackTemplateId") + .HasColumnType("text"); + + b.Property("Competition") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Division") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ExternalHostId") + .HasColumnType("character varying(40)"); + + b.Property("FeedbackConfig") + .HasColumnType("text"); + + b.Property("FeedbackTemplateId") + .HasColumnType("text"); + + b.Property("GameEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("GameMarkdown") + .HasColumnType("text"); + + b.Property("GameStart") + .HasColumnType("timestamp with time zone"); + + b.Property("GamespaceLimitPerSession") + .HasColumnType("integer"); + + b.Property("IsFeatured") + .HasColumnType("boolean"); + + b.Property("IsPublished") + .HasColumnType("boolean"); + + b.Property("Logo") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("MaxAttempts") + .HasColumnType("integer"); + + b.Property("MaxTeamSize") + .HasColumnType("integer"); + + b.Property("MinTeamSize") + .HasColumnType("integer"); + + b.Property("Mode") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PlayerMode") + .HasColumnType("integer"); + + b.Property("PracticeCertificateTemplateId") + .HasColumnType("text"); + + b.Property("RegistrationClose") + .HasColumnType("timestamp with time zone"); + + b.Property("RegistrationConstraint") + .HasColumnType("text"); + + b.Property("RegistrationMarkdown") + .HasColumnType("text"); + + b.Property("RegistrationOpen") + .HasColumnType("timestamp with time zone"); + + b.Property("RegistrationType") + .HasColumnType("integer"); + + b.Property("RequireSponsoredTeam") + .HasColumnType("boolean"); + + b.Property("RequireSynchronizedStart") + .HasColumnType("boolean"); + + b.Property("Season") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("SessionAvailabilityWarningThreshold") + .HasColumnType("integer"); + + b.Property("SessionLimit") + .HasColumnType("integer"); + + b.Property("SessionMinutes") + .HasColumnType("integer"); + + b.Property("ShowOnHomePageInPracticeMode") + .HasColumnType("boolean"); + + b.Property("Sponsor") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TextSearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Name", "Competition", "Id", "Track", "Season", "Division" }); + + b.Property("Track") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CertificateTemplateId"); + + b.HasIndex("ChallengesFeedbackTemplateId"); + + b.HasIndex("ExternalHostId"); + + b.HasIndex("FeedbackTemplateId"); + + b.HasIndex("PracticeCertificateTemplateId"); + + b.HasIndex("TextSearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("TextSearchVector"), "GIN"); + + b.ToTable("Games"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.GameExportBatch", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ExportedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("ExportedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ExportedByUserId"); + + b.ToTable("GameExportBatches", (string)null); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualBonus", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EnteredByUserId") + .HasColumnType("character varying(40)"); + + b.Property("EnteredOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PointValue") + .HasColumnType("double precision"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EnteredByUserId"); + + b.ToTable("ManualBonuses"); + + b.HasDiscriminator("Type"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Player", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Advanced") + .HasColumnType("boolean"); + + b.Property("AdvancedFromGameId") + .HasColumnType("character varying(40)"); + + b.Property("AdvancedFromPlayerId") + .HasColumnType("character varying(40)"); + + b.Property("AdvancedFromTeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AdvancedWithScore") + .HasColumnType("double precision"); + + b.Property("ApprovedName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CorrectCount") + .HasColumnType("integer"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("InviteCode") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("IsLateStart") + .HasColumnType("boolean"); + + b.Property("IsReady") + .HasColumnType("boolean"); + + b.Property("Mode") + .HasColumnType("integer"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NameStatus") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PartialCount") + .HasColumnType("integer"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("SessionBegin") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionMinutes") + .HasColumnType("double precision"); + + b.Property("SponsorId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Time") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("WhenCreated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AdvancedFromGameId"); + + b.HasIndex("AdvancedFromPlayerId"); + + b.HasIndex("GameId"); + + b.HasIndex("SponsorId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.HasIndex("Id", "TeamId"); + + b.HasIndex("UserId", "TeamId"); + + b.ToTable("Players"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroup", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsFeatured") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("ParentGroupId") + .HasColumnType("text"); + + b.Property("TextSearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Name", "Id", "Description" }); + + b.Property("UpdatedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("ParentGroupId"); + + b.HasIndex("TextSearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("TextSearchVector"), "GIN"); + + b.HasIndex("UpdatedByUserId"); + + b.ToTable("PracticeChallengeGroups"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroupChallengeSpec", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ChallengeSpecId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("PracticeChallengeGroupId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasAlternateKey("ChallengeSpecId", "PracticeChallengeGroupId"); + + b.HasIndex("PracticeChallengeGroupId"); + + b.ToTable("PracticeChallengeGroupChallengeSpec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeModeSettings", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AttemptLimit") + .HasColumnType("integer"); + + b.Property("CertificateTemplateId") + .HasColumnType("text"); + + b.Property("DefaultPracticeSessionLengthMinutes") + .HasColumnType("integer"); + + b.Property("IntroTextMarkdown") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("MaxConcurrentPracticeSessions") + .HasColumnType("integer"); + + b.Property("MaxPracticeSessionLengthMinutes") + .HasColumnType("integer"); + + b.Property("SuggestedSearches") + .HasColumnType("text"); + + b.Property("UpdatedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CertificateTemplateId") + .IsUnique(); + + b.HasIndex("UpdatedByUserId") + .IsUnique(); + + b.ToTable("PracticeModeSettings"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedCertificate", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Mode") + .HasColumnType("integer"); + + b.Property("OwnerUserId") + .HasColumnType("character varying(40)"); + + b.Property("PublishedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("PublishedCertificate"); + + b.HasDiscriminator("Mode"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Sponsor", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("Logo") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentSponsorId") + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("ParentSponsorId"); + + b.ToTable("Sponsors"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettings", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("SupportPageGreeting") + .HasColumnType("text"); + + b.Property("UpdatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("UpdatedByUserId") + .IsUnique(); + + b.ToTable("SupportSettings"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettingsAutoTag", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConditionType") + .HasColumnType("integer"); + + b.Property("ConditionValue") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("SupportSettingsId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Tag") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("SupportSettingsId"); + + b.ToTable("SupportSettingsAutoTags"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotification", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("EndsOn") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDismissible") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownContent") + .IsRequired() + .HasColumnType("text"); + + b.Property("NotificationType") + .HasColumnType("integer"); + + b.Property("StartsOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.ToTable("SystemNotifications"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotificationInteraction", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DismissedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("SawCalloutOn") + .HasColumnType("timestamp with time zone"); + + b.Property("SawFullNotificationOn") + .HasColumnType("timestamp with time zone"); + + b.Property("SystemNotificationId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasAlternateKey("SystemNotificationId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("SystemNotificationInteractions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Ticket", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AssigneeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("ChallengeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Key") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Key")); + + b.Property("Label") + .HasColumnType("text"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("RequesterId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("StaffCreated") + .HasColumnType("boolean"); + + b.Property("Status") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("AssigneeId"); + + b.HasIndex("ChallengeId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("Key") + .IsUnique(); + + b.HasIndex("PlayerId"); + + b.HasIndex("RequesterId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.TicketActivity", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AssigneeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TicketId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("AssigneeId"); + + b.HasIndex("TicketId"); + + b.HasIndex("UserId"); + + b.ToTable("TicketActivity"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.User", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ApprovedName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasDefaultSponsor") + .HasColumnType("boolean"); + + b.Property("LastIdpAssignedRole") + .HasColumnType("integer"); + + b.Property("LastLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LoginCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NameStatus") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayAudioOnBrowserNotification") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("SponsorId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Username") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("SponsorId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonusCompleteSolveRank", b => + { + b.HasBaseType("Gameboard.Api.Data.ChallengeBonus"); + + b.Property("SolveRank") + .HasColumnType("integer"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionChallengeSpec", b => + { + b.HasBaseType("Gameboard.Api.Data.FeedbackSubmission"); + + b.Property("ChallengeSpecId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasIndex("ChallengeSpecId"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionGame", b => + { + b.HasBaseType("Gameboard.Api.Data.FeedbackSubmission"); + + b.Property("GameId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasIndex("GameId"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualChallengeBonus", b => + { + b.HasBaseType("Gameboard.Api.Data.ManualBonus"); + + b.Property("ChallengeId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasIndex("ChallengeId"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualTeamBonus", b => + { + b.HasBaseType("Gameboard.Api.Data.ManualBonus"); + + b.Property("TeamId") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedCompetitiveCertificate", b => + { + b.HasBaseType("Gameboard.Api.Data.PublishedCertificate"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasIndex("GameId"); + + b.HasIndex("OwnerUserId"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedPracticeCertificate", b => + { + b.HasBaseType("Gameboard.Api.Data.PublishedCertificate"); + + b.Property("ChallengeSpecId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasIndex("ChallengeSpecId"); + + b.HasIndex("OwnerUserId"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("GameGameExportBatch", b => + { + b.HasOne("Gameboard.Api.Data.GameExportBatch", null) + .WithMany() + .HasForeignKey("ExportedInBatchesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.Game", null) + .WithMany() + .HasForeignKey("IncludedGamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ApiKey", b => + { + b.HasOne("Gameboard.Api.Data.User", "Owner") + .WithMany("ApiKeys") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.AwardedChallengeBonus", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeBonus", "ChallengeBonus") + .WithMany("AwardedTo") + .HasForeignKey("ChallengeBonusId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("AwardedBonuses") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Challenge"); + + b.Navigation("ChallengeBonus"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.CertificateTemplate", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedCertificateTemplates") + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Challenge", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Challenges") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.Player", "Player") + .WithMany("Challenges") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "Spec") + .WithMany("Challenges") + .HasForeignKey("SpecId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Game"); + + b.Navigation("Player"); + + b.Navigation("Spec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonus", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("Bonuses") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ChallengeSpec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeEvent", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Events") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeGate", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Prerequisites") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSpec", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Specs") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSubmission", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Submissions") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.DenormalizedTeamScore", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("DenormalizedTeamScores") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameTeam", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("ExternalGameTeams") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Feedback", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Feedback") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("Feedback") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Feedback") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.Player", "Player") + .WithMany("Feedback") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("Feedback") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Challenge"); + + b.Navigation("ChallengeSpec"); + + b.Navigation("Game"); + + b.Navigation("Player"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmission", b => + { + b.HasOne("Gameboard.Api.Data.FeedbackTemplate", "FeedbackTemplate") + .WithMany("Submissions") + .HasForeignKey("FeedbackTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("FeedbackSubmissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsMany("Gameboard.Api.Features.Feedback.QuestionSubmission", "Responses", b1 => + { + b1.Property("FeedbackSubmissionId") + .HasColumnType("text"); + + b1.Property("Id") + .HasColumnType("text"); + + b1.Property("Answer") + .HasColumnType("text"); + + b1.Property("Prompt") + .HasColumnType("text"); + + b1.Property("ShortName") + .HasColumnType("text"); + + b1.HasKey("FeedbackSubmissionId", "Id"); + + b1.ToTable("FeedbackSubmissionResponses", (string)null); + + b1.WithOwner() + .HasForeignKey("FeedbackSubmissionId"); + }); + + b.Navigation("FeedbackTemplate"); + + b.Navigation("Responses"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackTemplate", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedFeedbackTemplates") + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Game", b => + { + b.HasOne("Gameboard.Api.Data.CertificateTemplate", "CertificateTemplate") + .WithMany("UseAsTemplateForGames") + .HasForeignKey("CertificateTemplateId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.FeedbackTemplate", "ChallengesFeedbackTemplate") + .WithMany("UseAsFeedbackTemplateForGames") + .HasForeignKey("ChallengesFeedbackTemplateId"); + + b.HasOne("Gameboard.Api.Data.ExternalGameHost", "ExternalHost") + .WithMany("UsedByGames") + .HasForeignKey("ExternalHostId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.FeedbackTemplate", "FeedbackTemplate") + .WithMany("UseAsFeedbackTemplateForGameChallenges") + .HasForeignKey("FeedbackTemplateId"); + + b.HasOne("Gameboard.Api.Data.CertificateTemplate", "PracticeCertificateTemplate") + .WithMany("UseAsPracticeTemplateForGames") + .HasForeignKey("PracticeCertificateTemplateId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CertificateTemplate"); + + b.Navigation("ChallengesFeedbackTemplate"); + + b.Navigation("ExternalHost"); + + b.Navigation("FeedbackTemplate"); + + b.Navigation("PracticeCertificateTemplate"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.GameExportBatch", b => + { + b.HasOne("Gameboard.Api.Data.User", "ExportedByUser") + .WithMany("GameExportBatches") + .HasForeignKey("ExportedByUserId"); + + b.Navigation("ExportedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualBonus", b => + { + b.HasOne("Gameboard.Api.Data.User", "EnteredByUser") + .WithMany("EnteredManualBonuses") + .HasForeignKey("EnteredByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EnteredByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Player", b => + { + b.HasOne("Gameboard.Api.Data.Game", "AdvancedFromGame") + .WithMany("AdvancedPlayers") + .HasForeignKey("AdvancedFromGameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.Player", "AdvancedFromPlayer") + .WithMany("AdvancedToPlayers") + .HasForeignKey("AdvancedFromPlayerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Players") + .HasForeignKey("GameId"); + + b.HasOne("Gameboard.Api.Data.Sponsor", "Sponsor") + .WithMany("SponsoredPlayers") + .HasForeignKey("SponsorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("Enrollments") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("AdvancedFromGame"); + + b.Navigation("AdvancedFromPlayer"); + + b.Navigation("Game"); + + b.Navigation("Sponsor"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroup", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedPracticeChallengeGroups") + .HasForeignKey("CreatedByUserId"); + + b.HasOne("Gameboard.Api.Data.PracticeChallengeGroup", "ParentGroup") + .WithMany("ChildGroups") + .HasForeignKey("ParentGroupId"); + + b.HasOne("Gameboard.Api.Data.User", "UpdatedByUser") + .WithMany("UpdatedPracticeChallengeGroups") + .HasForeignKey("UpdatedByUserId"); + + b.Navigation("CreatedByUser"); + + b.Navigation("ParentGroup"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroupChallengeSpec", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("PracticeChallengeGroups") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.PracticeChallengeGroup", "PracticeChallengeGroup") + .WithMany("ChallengeSpecs") + .HasForeignKey("PracticeChallengeGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeSpec"); + + b.Navigation("PracticeChallengeGroup"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeModeSettings", b => + { + b.HasOne("Gameboard.Api.Data.CertificateTemplate", "CertificateTemplate") + .WithOne("UsedAsPracticeModeDefault") + .HasForeignKey("Gameboard.Api.Data.PracticeModeSettings", "CertificateTemplateId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.User", "UpdatedByUser") + .WithOne("UpdatedPracticeModeSettings") + .HasForeignKey("Gameboard.Api.Data.PracticeModeSettings", "UpdatedByUserId"); + + b.Navigation("CertificateTemplate"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Sponsor", b => + { + b.HasOne("Gameboard.Api.Data.Sponsor", "ParentSponsor") + .WithMany("ChildSponsors") + .HasForeignKey("ParentSponsorId"); + + b.Navigation("ParentSponsor"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettings", b => + { + b.HasOne("Gameboard.Api.Data.User", "UpdatedByUser") + .WithOne("UpdatedSupportSettings") + .HasForeignKey("Gameboard.Api.Data.SupportSettings", "UpdatedByUserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettingsAutoTag", b => + { + b.HasOne("Gameboard.Api.Data.SupportSettings", "SupportSettings") + .WithMany("AutoTags") + .HasForeignKey("SupportSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SupportSettings"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotification", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedSystemNotifications") + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotificationInteraction", b => + { + b.HasOne("Gameboard.Api.Data.SystemNotification", "SystemNotification") + .WithMany("Interactions") + .HasForeignKey("SystemNotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("SystemNotificationInteractions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SystemNotification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Ticket", b => + { + b.HasOne("Gameboard.Api.Data.User", "Assignee") + .WithMany() + .HasForeignKey("AssigneeId"); + + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Tickets") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.HasOne("Gameboard.Api.Data.Player", "Player") + .WithMany("Tickets") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.User", "Requester") + .WithMany() + .HasForeignKey("RequesterId"); + + b.Navigation("Assignee"); + + b.Navigation("Challenge"); + + b.Navigation("Creator"); + + b.Navigation("Player"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.TicketActivity", b => + { + b.HasOne("Gameboard.Api.Data.User", "Assignee") + .WithMany() + .HasForeignKey("AssigneeId"); + + b.HasOne("Gameboard.Api.Data.Ticket", "Ticket") + .WithMany("Activity") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Assignee"); + + b.Navigation("Ticket"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.User", b => + { + b.HasOne("Gameboard.Api.Data.Sponsor", "Sponsor") + .WithMany("SponsoredUsers") + .HasForeignKey("SponsorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sponsor"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionChallengeSpec", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("FeedbackSubmissions") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeSpec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionGame", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("FeedbackSubmissions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualChallengeBonus", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("AwardedManualBonuses") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedCompetitiveCertificate", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("PublishedCompetitiveCertificates") + .HasForeignKey("GameId"); + + b.HasOne("Gameboard.Api.Data.User", "OwnerUser") + .WithMany("PublishedCompetitiveCertificates") + .HasForeignKey("OwnerUserId") + .HasConstraintName("FK_OwnerUserId_Users_Id"); + + b.Navigation("Game"); + + b.Navigation("OwnerUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedPracticeCertificate", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("PublishedPracticeCertificates") + .HasForeignKey("ChallengeSpecId"); + + b.HasOne("Gameboard.Api.Data.User", "OwnerUser") + .WithMany("PublishedPracticeCertificates") + .HasForeignKey("OwnerUserId") + .HasConstraintName("FK_OwnerUserId_Users_Id"); + + b.Navigation("ChallengeSpec"); + + b.Navigation("OwnerUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.CertificateTemplate", b => + { + b.Navigation("UseAsPracticeTemplateForGames"); + + b.Navigation("UseAsTemplateForGames"); + + b.Navigation("UsedAsPracticeModeDefault"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Challenge", b => + { + b.Navigation("AwardedBonuses"); + + b.Navigation("AwardedManualBonuses"); + + b.Navigation("Events"); + + b.Navigation("Feedback"); + + b.Navigation("Submissions"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonus", b => + { + b.Navigation("AwardedTo"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSpec", b => + { + b.Navigation("Bonuses"); + + b.Navigation("Challenges"); + + b.Navigation("Feedback"); + + b.Navigation("FeedbackSubmissions"); + + b.Navigation("PracticeChallengeGroups"); + + b.Navigation("PublishedPracticeCertificates"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameHost", b => + { + b.Navigation("UsedByGames"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackTemplate", b => + { + b.Navigation("Submissions"); + + b.Navigation("UseAsFeedbackTemplateForGameChallenges"); + + b.Navigation("UseAsFeedbackTemplateForGames"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Game", b => + { + b.Navigation("AdvancedPlayers"); + + b.Navigation("Challenges"); + + b.Navigation("DenormalizedTeamScores"); + + b.Navigation("ExternalGameTeams"); + + b.Navigation("Feedback"); + + b.Navigation("FeedbackSubmissions"); + + b.Navigation("Players"); + + b.Navigation("Prerequisites"); + + b.Navigation("PublishedCompetitiveCertificates"); + + b.Navigation("Specs"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Player", b => + { + b.Navigation("AdvancedToPlayers"); + + b.Navigation("Challenges"); + + b.Navigation("Feedback"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroup", b => + { + b.Navigation("ChallengeSpecs"); + + b.Navigation("ChildGroups"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Sponsor", b => + { + b.Navigation("ChildSponsors"); + + b.Navigation("SponsoredPlayers"); + + b.Navigation("SponsoredUsers"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettings", b => + { + b.Navigation("AutoTags"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotification", b => + { + b.Navigation("Interactions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Ticket", b => + { + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.User", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("CreatedCertificateTemplates"); + + b.Navigation("CreatedFeedbackTemplates"); + + b.Navigation("CreatedPracticeChallengeGroups"); + + b.Navigation("CreatedSystemNotifications"); + + b.Navigation("Enrollments"); + + b.Navigation("EnteredManualBonuses"); + + b.Navigation("Feedback"); + + b.Navigation("FeedbackSubmissions"); + + b.Navigation("GameExportBatches"); + + b.Navigation("PublishedCompetitiveCertificates"); + + b.Navigation("PublishedPracticeCertificates"); + + b.Navigation("SystemNotificationInteractions"); + + b.Navigation("UpdatedPracticeChallengeGroups"); + + b.Navigation("UpdatedPracticeModeSettings"); + + b.Navigation("UpdatedSupportSettings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Gameboard.Api/Migrations/20251202163308_dotnet10_version_update.cs b/src/Gameboard.Api/Migrations/20251202163308_dotnet10_version_update.cs new file mode 100644 index 00000000..2619142c --- /dev/null +++ b/src/Gameboard.Api/Migrations/20251202163308_dotnet10_version_update.cs @@ -0,0 +1,1584 @@ +// Copyright 2025 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. + +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Gameboard.Api.Migrations +{ + /// + public partial class dotnet10_version_update : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ArchivedChallenges", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + TeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Name = table.Column(type: "text", nullable: true), + Tag = table.Column(type: "text", nullable: true), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + GameName = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + PlayerId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + PlayerName = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + UserId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + StartTime = table.Column(type: "timestamp with time zone", nullable: false), + EndTime = table.Column(type: "timestamp with time zone", nullable: false), + LastScoreTime = table.Column(type: "timestamp with time zone", nullable: false), + LastSyncTime = table.Column(type: "timestamp with time zone", nullable: false), + HasGamespaceDeployed = table.Column(type: "boolean", nullable: false), + PlayerMode = table.Column(type: "integer", nullable: false), + State = table.Column(type: "text", nullable: true), + Points = table.Column(type: "integer", nullable: false), + Score = table.Column(type: "integer", nullable: false), + Duration = table.Column(type: "bigint", nullable: false), + Result = table.Column(type: "integer", nullable: false), + Events = table.Column(type: "text", nullable: true), + Submissions = table.Column(type: "text", nullable: true), + TeamMembers = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ArchivedChallenges", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Extensions", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Type = table.Column(type: "integer", nullable: false), + HostUrl = table.Column(type: "text", nullable: false), + Token = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + IsEnabled = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Extensions", x => x.Id); + table.UniqueConstraint("AK_Extensions_Type", x => x.Type); + }); + + migrationBuilder.CreateTable( + name: "ExternalGameHosts", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + ClientUrl = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + DestroyResourcesOnDeployFailure = table.Column(type: "boolean", nullable: false), + GamespaceDeployBatchSize = table.Column(type: "integer", nullable: true), + HttpTimeoutInSeconds = table.Column(type: "integer", nullable: true), + HostApiKey = table.Column(type: "character varying(70)", maxLength: 70, nullable: true), + HostUrl = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + PingEndpoint = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + StartupEndpoint = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + TeamExtendedEndpoint = table.Column(type: "character varying(200)", maxLength: 200, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ExternalGameHosts", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Sponsors", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Name = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + Logo = table.Column(type: "text", nullable: true), + Approved = table.Column(type: "boolean", nullable: false), + ParentSponsorId = table.Column(type: "character varying(40)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Sponsors", x => x.Id); + table.ForeignKey( + name: "FK_Sponsors_Sponsors_ParentSponsorId", + column: x => x.ParentSponsorId, + principalTable: "Sponsors", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Username = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Email = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + NameStatus = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + ApprovedName = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Role = table.Column(type: "integer", nullable: false), + CreatedOn = table.Column(type: "timestamp with time zone", nullable: false), + LastLoginDate = table.Column(type: "timestamp with time zone", nullable: true), + LastIdpAssignedRole = table.Column(type: "integer", nullable: true), + LoginCount = table.Column(type: "integer", nullable: false, defaultValue: 0), + HasDefaultSponsor = table.Column(type: "boolean", nullable: false), + PlayAudioOnBrowserNotification = table.Column(type: "boolean", nullable: false, defaultValue: false), + SponsorId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + table.ForeignKey( + name: "FK_Users_Sponsors_SponsorId", + column: x => x.SponsorId, + principalTable: "Sponsors", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ApiKeys", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + GeneratedOn = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "NOW()"), + ExpiresOn = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "NULL"), + Key = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + OwnerId = table.Column(type: "character varying(40)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApiKeys", x => x.Id); + table.ForeignKey( + name: "FK_ApiKeys_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "CertificateTemplate", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Content = table.Column(type: "text", nullable: false), + CreatedByUserId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CertificateTemplate", x => x.Id); + table.ForeignKey( + name: "FK_CertificateTemplate_Users_CreatedByUserId", + column: x => x.CreatedByUserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "FeedbackTemplates", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + HelpText = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Content = table.Column(type: "text", nullable: false), + CreatedByUserId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_FeedbackTemplates", x => x.Id); + table.ForeignKey( + name: "FK_FeedbackTemplates_Users_CreatedByUserId", + column: x => x.CreatedByUserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "GameExportBatches", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ExportedOn = table.Column(type: "timestamp with time zone", nullable: false), + ExportedByUserId = table.Column(type: "character varying(40)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_GameExportBatches", x => x.Id); + table.ForeignKey( + name: "FK_GameExportBatches_Users_ExportedByUserId", + column: x => x.ExportedByUserId, + principalTable: "Users", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "PracticeChallengeGroups", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + IsFeatured = table.Column(type: "boolean", nullable: false), + ImageUrl = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + TextSearchVector = table.Column(type: "tsvector", nullable: true) + .Annotation("Npgsql:TsVectorConfig", "english") + .Annotation("Npgsql:TsVectorProperties", new[] { "Name", "Id", "Description" }), + ParentGroupId = table.Column(type: "text", nullable: true), + CreatedOn = table.Column(type: "timestamp with time zone", nullable: false), + CreatedByUserId = table.Column(type: "character varying(40)", nullable: true), + UpdatedOn = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedByUserId = table.Column(type: "character varying(40)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PracticeChallengeGroups", x => x.Id); + table.ForeignKey( + name: "FK_PracticeChallengeGroups_PracticeChallengeGroups_ParentGroup~", + column: x => x.ParentGroupId, + principalTable: "PracticeChallengeGroups", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_PracticeChallengeGroups_Users_CreatedByUserId", + column: x => x.CreatedByUserId, + principalTable: "Users", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_PracticeChallengeGroups_Users_UpdatedByUserId", + column: x => x.UpdatedByUserId, + principalTable: "Users", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "SupportSettings", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + SupportPageGreeting = table.Column(type: "text", nullable: true), + UpdatedOn = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedByUserId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SupportSettings", x => x.Id); + table.ForeignKey( + name: "FK_SupportSettings_Users_UpdatedByUserId", + column: x => x.UpdatedByUserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "SystemNotifications", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Title = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + MarkdownContent = table.Column(type: "text", nullable: false), + StartsOn = table.Column(type: "timestamp with time zone", nullable: true), + EndsOn = table.Column(type: "timestamp with time zone", nullable: true), + NotificationType = table.Column(type: "integer", nullable: false), + IsDeleted = table.Column(type: "boolean", nullable: false), + IsDismissible = table.Column(type: "boolean", nullable: false, defaultValue: true), + CreatedByUserId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SystemNotifications", x => x.Id); + table.ForeignKey( + name: "FK_SystemNotifications_Users_CreatedByUserId", + column: x => x.CreatedByUserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PracticeModeSettings", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + AttemptLimit = table.Column(type: "integer", nullable: true), + DefaultPracticeSessionLengthMinutes = table.Column(type: "integer", nullable: false), + IntroTextMarkdown = table.Column(type: "character varying(4000)", maxLength: 4000, nullable: true), + MaxConcurrentPracticeSessions = table.Column(type: "integer", nullable: true), + MaxPracticeSessionLengthMinutes = table.Column(type: "integer", nullable: true), + SuggestedSearches = table.Column(type: "text", nullable: true), + UpdatedOn = table.Column(type: "timestamp with time zone", nullable: true), + CertificateTemplateId = table.Column(type: "text", nullable: true), + UpdatedByUserId = table.Column(type: "character varying(40)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PracticeModeSettings", x => x.Id); + table.ForeignKey( + name: "FK_PracticeModeSettings_CertificateTemplate_CertificateTemplat~", + column: x => x.CertificateTemplateId, + principalTable: "CertificateTemplate", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_PracticeModeSettings_Users_UpdatedByUserId", + column: x => x.UpdatedByUserId, + principalTable: "Users", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Games", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Competition = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Season = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Track = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Division = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Logo = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Sponsor = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Background = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + GameStart = table.Column(type: "timestamp with time zone", nullable: false), + GameEnd = table.Column(type: "timestamp with time zone", nullable: false), + GameMarkdown = table.Column(type: "text", nullable: true), + FeedbackConfig = table.Column(type: "text", nullable: true), + RegistrationMarkdown = table.Column(type: "text", nullable: true), + RegistrationOpen = table.Column(type: "timestamp with time zone", nullable: false), + RegistrationClose = table.Column(type: "timestamp with time zone", nullable: false), + RegistrationType = table.Column(type: "integer", nullable: false), + RegistrationConstraint = table.Column(type: "text", nullable: true), + MinTeamSize = table.Column(type: "integer", nullable: false), + MaxTeamSize = table.Column(type: "integer", nullable: false), + MaxAttempts = table.Column(type: "integer", nullable: false), + RequireSponsoredTeam = table.Column(type: "boolean", nullable: false), + SessionMinutes = table.Column(type: "integer", nullable: false), + SessionLimit = table.Column(type: "integer", nullable: false), + SessionAvailabilityWarningThreshold = table.Column(type: "integer", nullable: true), + GamespaceLimitPerSession = table.Column(type: "integer", nullable: false), + IsPublished = table.Column(type: "boolean", nullable: false), + AllowLateStart = table.Column(type: "boolean", nullable: false), + AllowPreview = table.Column(type: "boolean", nullable: false), + AllowPublicScoreboardAccess = table.Column(type: "boolean", nullable: false), + AllowReset = table.Column(type: "boolean", nullable: false), + CardText1 = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + CardText2 = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + CardText3 = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + IsFeatured = table.Column(type: "boolean", nullable: false), + ExternalHostId = table.Column(type: "character varying(40)", nullable: true), + Mode = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + PlayerMode = table.Column(type: "integer", nullable: false), + RequireSynchronizedStart = table.Column(type: "boolean", nullable: false), + ShowOnHomePageInPracticeMode = table.Column(type: "boolean", nullable: false), + TextSearchVector = table.Column(type: "tsvector", nullable: true) + .Annotation("Npgsql:TsVectorConfig", "english") + .Annotation("Npgsql:TsVectorProperties", new[] { "Name", "Competition", "Id", "Track", "Season", "Division" }), + ChallengesFeedbackTemplateId = table.Column(type: "text", nullable: true), + FeedbackTemplateId = table.Column(type: "text", nullable: true), + CertificateTemplateId = table.Column(type: "text", nullable: true), + PracticeCertificateTemplateId = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Games", x => x.Id); + table.ForeignKey( + name: "FK_Games_CertificateTemplate_CertificateTemplateId", + column: x => x.CertificateTemplateId, + principalTable: "CertificateTemplate", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Games_CertificateTemplate_PracticeCertificateTemplateId", + column: x => x.PracticeCertificateTemplateId, + principalTable: "CertificateTemplate", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Games_ExternalGameHosts_ExternalHostId", + column: x => x.ExternalHostId, + principalTable: "ExternalGameHosts", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Games_FeedbackTemplates_ChallengesFeedbackTemplateId", + column: x => x.ChallengesFeedbackTemplateId, + principalTable: "FeedbackTemplates", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Games_FeedbackTemplates_FeedbackTemplateId", + column: x => x.FeedbackTemplateId, + principalTable: "FeedbackTemplates", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "SupportSettingsAutoTags", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ConditionType = table.Column(type: "integer", nullable: false), + ConditionValue = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + IsEnabled = table.Column(type: "boolean", nullable: false), + Tag = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + SupportSettingsId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SupportSettingsAutoTags", x => x.Id); + table.ForeignKey( + name: "FK_SupportSettingsAutoTags_SupportSettings_SupportSettingsId", + column: x => x.SupportSettingsId, + principalTable: "SupportSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "SystemNotificationInteractions", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + SawCalloutOn = table.Column(type: "timestamp with time zone", nullable: true), + SawFullNotificationOn = table.Column(type: "timestamp with time zone", nullable: true), + DismissedOn = table.Column(type: "timestamp with time zone", nullable: true), + SystemNotificationId = table.Column(type: "character varying(40)", nullable: false), + UserId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SystemNotificationInteractions", x => x.Id); + table.UniqueConstraint("AK_SystemNotificationInteractions_SystemNotificationId_UserId", x => new { x.SystemNotificationId, x.UserId }); + table.ForeignKey( + name: "FK_SystemNotificationInteractions_SystemNotifications_SystemNo~", + column: x => x.SystemNotificationId, + principalTable: "SystemNotifications", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_SystemNotificationInteractions_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ChallengeGates", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + TargetId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + RequiredId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + RequiredScore = table.Column(type: "double precision", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeGates", x => x.Id); + table.ForeignKey( + name: "FK_ChallengeGates_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ChallengeSpecs", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + ExternalId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Tag = table.Column(type: "text", nullable: true), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Text = table.Column(type: "text", nullable: true), + Disabled = table.Column(type: "boolean", nullable: false), + AverageDeploySeconds = table.Column(type: "integer", nullable: false), + Points = table.Column(type: "integer", nullable: false), + X = table.Column(type: "real", nullable: false), + Y = table.Column(type: "real", nullable: false), + R = table.Column(type: "real", nullable: false), + GameEngineType = table.Column(type: "integer", nullable: false), + SolutionGuideUrl = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + ShowSolutionGuideInCompetitiveMode = table.Column(type: "boolean", nullable: false), + Tags = table.Column(type: "text", nullable: true), + IsHidden = table.Column(type: "boolean", nullable: false), + TextSearchVector = table.Column(type: "tsvector", nullable: true) + .Annotation("Npgsql:TsVectorConfig", "english") + .Annotation("Npgsql:TsVectorProperties", new[] { "Name", "Id", "Description", "GameId", "Tag", "Tags", "Text" }), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeSpecs", x => x.Id); + table.ForeignKey( + name: "FK_ChallengeSpecs_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "DenormalizedTeamScores", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + TeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + TeamName = table.Column(type: "text", nullable: true), + Rank = table.Column(type: "integer", nullable: false), + ScoreOverall = table.Column(type: "double precision", nullable: false), + ScoreAutoBonus = table.Column(type: "double precision", nullable: false), + ScoreManualBonus = table.Column(type: "double precision", nullable: false), + ScoreChallenge = table.Column(type: "double precision", nullable: false), + ScoreAdvanced = table.Column(type: "double precision", nullable: true), + SolveCountNone = table.Column(type: "integer", nullable: false), + SolveCountPartial = table.Column(type: "integer", nullable: false), + SolveCountComplete = table.Column(type: "integer", nullable: false), + CumulativeTimeMs = table.Column(type: "double precision", nullable: false), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DenormalizedTeamScores", x => x.Id); + table.ForeignKey( + name: "FK_DenormalizedTeamScores_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ExternalGameTeams", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + TeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + ExternalGameUrl = table.Column(type: "text", nullable: true), + DeployStatus = table.Column(type: "integer", nullable: false), + GameId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExternalGameTeams", x => x.Id); + table.UniqueConstraint("AK_ExternalGameTeams_TeamId_GameId", x => new { x.TeamId, x.GameId }); + table.ForeignKey( + name: "FK_ExternalGameTeams_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "GameGameExportBatch", + columns: table => new + { + ExportedInBatchesId = table.Column(type: "text", nullable: false), + IncludedGamesId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameGameExportBatch", x => new { x.ExportedInBatchesId, x.IncludedGamesId }); + table.ForeignKey( + name: "FK_GameGameExportBatch_GameExportBatches_ExportedInBatchesId", + column: x => x.ExportedInBatchesId, + principalTable: "GameExportBatches", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GameGameExportBatch_Games_IncludedGamesId", + column: x => x.IncludedGamesId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Players", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + TeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + UserId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + ApprovedName = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + NameStatus = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + InviteCode = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + IsReady = table.Column(type: "boolean", nullable: false), + Role = table.Column(type: "integer", nullable: false), + SessionBegin = table.Column(type: "timestamp with time zone", nullable: false), + SessionEnd = table.Column(type: "timestamp with time zone", nullable: false), + SessionMinutes = table.Column(type: "double precision", nullable: false), + Rank = table.Column(type: "integer", nullable: false), + Score = table.Column(type: "integer", nullable: false), + Time = table.Column(type: "bigint", nullable: false), + CorrectCount = table.Column(type: "integer", nullable: false), + PartialCount = table.Column(type: "integer", nullable: false), + Advanced = table.Column(type: "boolean", nullable: false), + Mode = table.Column(type: "integer", nullable: false), + WhenCreated = table.Column(type: "timestamp with time zone", nullable: false), + IsLateStart = table.Column(type: "boolean", nullable: false), + AdvancedFromGameId = table.Column(type: "character varying(40)", nullable: true), + AdvancedFromPlayerId = table.Column(type: "character varying(40)", nullable: true), + AdvancedFromTeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + AdvancedWithScore = table.Column(type: "double precision", nullable: true), + SponsorId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Players", x => x.Id); + table.ForeignKey( + name: "FK_Players_Games_AdvancedFromGameId", + column: x => x.AdvancedFromGameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Players_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Players_Players_AdvancedFromPlayerId", + column: x => x.AdvancedFromPlayerId, + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Players_Sponsors_SponsorId", + column: x => x.SponsorId, + principalTable: "Sponsors", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Players_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ChallengeBonuses", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Description = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + PointValue = table.Column(type: "double precision", nullable: false), + ChallengeBonusType = table.Column(type: "integer", nullable: false), + ChallengeSpecId = table.Column(type: "character varying(40)", nullable: true), + SolveRank = table.Column(type: "integer", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeBonuses", x => x.Id); + table.ForeignKey( + name: "FK_ChallengeBonuses_ChallengeSpecs_ChallengeSpecId", + column: x => x.ChallengeSpecId, + principalTable: "ChallengeSpecs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "FeedbackSubmissions", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + AttachedEntityType = table.Column(type: "integer", nullable: false), + WhenEdited = table.Column(type: "timestamp with time zone", nullable: true), + WhenFinalized = table.Column(type: "timestamp with time zone", nullable: true), + WhenCreated = table.Column(type: "timestamp with time zone", nullable: false), + FeedbackTemplateId = table.Column(type: "text", nullable: false), + UserId = table.Column(type: "character varying(40)", nullable: false), + ChallengeSpecId = table.Column(type: "character varying(40)", nullable: true), + GameId = table.Column(type: "character varying(40)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_FeedbackSubmissions", x => x.Id); + table.ForeignKey( + name: "FK_FeedbackSubmissions_ChallengeSpecs_ChallengeSpecId", + column: x => x.ChallengeSpecId, + principalTable: "ChallengeSpecs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_FeedbackSubmissions_FeedbackTemplates_FeedbackTemplateId", + column: x => x.FeedbackTemplateId, + principalTable: "FeedbackTemplates", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_FeedbackSubmissions_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_FeedbackSubmissions_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PracticeChallengeGroupChallengeSpec", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + PracticeChallengeGroupId = table.Column(type: "text", nullable: false), + ChallengeSpecId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PracticeChallengeGroupChallengeSpec", x => x.Id); + table.UniqueConstraint("AK_PracticeChallengeGroupChallengeSpec_ChallengeSpecId_Practic~", x => new { x.ChallengeSpecId, x.PracticeChallengeGroupId }); + table.ForeignKey( + name: "FK_PracticeChallengeGroupChallengeSpec_ChallengeSpecs_Challeng~", + column: x => x.ChallengeSpecId, + principalTable: "ChallengeSpecs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PracticeChallengeGroupChallengeSpec_PracticeChallengeGroups~", + column: x => x.PracticeChallengeGroupId, + principalTable: "PracticeChallengeGroups", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PublishedCertificate", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + PublishedOn = table.Column(type: "timestamp with time zone", nullable: false), + Mode = table.Column(type: "integer", nullable: false), + OwnerUserId = table.Column(type: "character varying(40)", nullable: true), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + ChallengeSpecId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PublishedCertificate", x => x.Id); + table.ForeignKey( + name: "FK_OwnerUserId_Users_Id", + column: x => x.OwnerUserId, + principalTable: "Users", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_PublishedCertificate_ChallengeSpecs_ChallengeSpecId", + column: x => x.ChallengeSpecId, + principalTable: "ChallengeSpecs", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_PublishedCertificate_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Challenges", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Name = table.Column(type: "text", nullable: true), + ExternalId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + TeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Tag = table.Column(type: "text", nullable: true), + GraderKey = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + State = table.Column(type: "text", nullable: true), + Points = table.Column(type: "integer", nullable: false), + Score = table.Column(type: "double precision", nullable: false), + PlayerMode = table.Column(type: "integer", nullable: false), + LastScoreTime = table.Column(type: "timestamp with time zone", nullable: false), + LastSyncTime = table.Column(type: "timestamp with time zone", nullable: false), + WhenCreated = table.Column(type: "timestamp with time zone", nullable: false), + StartTime = table.Column(type: "timestamp with time zone", nullable: false), + EndTime = table.Column(type: "timestamp with time zone", nullable: false), + HasDeployedGamespace = table.Column(type: "boolean", nullable: false), + GameEngineType = table.Column(type: "integer", nullable: false), + PendingSubmission = table.Column(type: "text", nullable: true), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + PlayerId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + SpecId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Challenges", x => x.Id); + table.ForeignKey( + name: "FK_Challenges_ChallengeSpecs_SpecId", + column: x => x.SpecId, + principalTable: "ChallengeSpecs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Challenges_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Challenges_Players_PlayerId", + column: x => x.PlayerId, + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "FeedbackSubmissionResponses", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + FeedbackSubmissionId = table.Column(type: "text", nullable: false), + Answer = table.Column(type: "text", nullable: true), + Prompt = table.Column(type: "text", nullable: true), + ShortName = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_FeedbackSubmissionResponses", x => new { x.FeedbackSubmissionId, x.Id }); + table.ForeignKey( + name: "FK_FeedbackSubmissionResponses_FeedbackSubmissions_FeedbackSub~", + column: x => x.FeedbackSubmissionId, + principalTable: "FeedbackSubmissions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AwardedChallengeBonuses", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + EnteredOn = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "NOW()"), + InternalSummary = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + ChallengeBonusId = table.Column(type: "character varying(40)", nullable: true), + ChallengeId = table.Column(type: "character varying(40)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AwardedChallengeBonuses", x => x.Id); + table.ForeignKey( + name: "FK_AwardedChallengeBonuses_ChallengeBonuses_ChallengeBonusId", + column: x => x.ChallengeBonusId, + principalTable: "ChallengeBonuses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_AwardedChallengeBonuses_Challenges_ChallengeId", + column: x => x.ChallengeId, + principalTable: "Challenges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ChallengeEvents", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + ChallengeId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + UserId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + TeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Text = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + Type = table.Column(type: "integer", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeEvents", x => x.Id); + table.ForeignKey( + name: "FK_ChallengeEvents_Challenges_ChallengeId", + column: x => x.ChallengeId, + principalTable: "Challenges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ChallengeSubmissions", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + SubmittedOn = table.Column(type: "timestamp with time zone", nullable: false), + Score = table.Column(type: "double precision", nullable: false, defaultValue: 0.0), + Answers = table.Column(type: "text", nullable: false), + ChallengeId = table.Column(type: "character varying(40)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeSubmissions", x => x.Id); + table.ForeignKey( + name: "FK_ChallengeSubmissions_Challenges_ChallengeId", + column: x => x.ChallengeId, + principalTable: "Challenges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Feedback", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + UserId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + PlayerId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + GameId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + ChallengeId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + ChallengeSpecId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Answers = table.Column(type: "text", nullable: true), + Submitted = table.Column(type: "boolean", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Feedback", x => x.Id); + table.ForeignKey( + name: "FK_Feedback_ChallengeSpecs_ChallengeSpecId", + column: x => x.ChallengeSpecId, + principalTable: "ChallengeSpecs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Feedback_Challenges_ChallengeId", + column: x => x.ChallengeId, + principalTable: "Challenges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Feedback_Games_GameId", + column: x => x.GameId, + principalTable: "Games", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Feedback_Players_PlayerId", + column: x => x.PlayerId, + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Feedback_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ManualBonuses", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Description = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + EnteredOn = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "NOW()"), + PointValue = table.Column(type: "double precision", nullable: false), + Type = table.Column(type: "integer", nullable: false), + EnteredByUserId = table.Column(type: "character varying(40)", nullable: true), + ChallengeId = table.Column(type: "character varying(40)", nullable: true), + TeamId = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ManualBonuses", x => x.Id); + table.ForeignKey( + name: "FK_ManualBonuses_Challenges_ChallengeId", + column: x => x.ChallengeId, + principalTable: "Challenges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ManualBonuses_Users_EnteredByUserId", + column: x => x.EnteredByUserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Tickets", + columns: table => new + { + Id = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + Key = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + RequesterId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + AssigneeId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + CreatorId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + ChallengeId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + PlayerId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + TeamId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Summary = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + Description = table.Column(type: "text", nullable: true), + Status = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Label = table.Column(type: "text", nullable: true), + StaffCreated = table.Column(type: "boolean", nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdated = table.Column(type: "timestamp with time zone", nullable: false), + Attachments = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tickets", x => x.Id); + table.ForeignKey( + name: "FK_Tickets_Challenges_ChallengeId", + column: x => x.ChallengeId, + principalTable: "Challenges", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Tickets_Players_PlayerId", + column: x => x.PlayerId, + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_Tickets_Users_AssigneeId", + column: x => x.AssigneeId, + principalTable: "Users", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Tickets_Users_CreatorId", + column: x => x.CreatorId, + principalTable: "Users", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Tickets_Users_RequesterId", + column: x => x.RequesterId, + principalTable: "Users", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "TicketActivity", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + TicketId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + UserId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + AssigneeId = table.Column(type: "character varying(40)", maxLength: 40, nullable: true), + Message = table.Column(type: "text", nullable: true), + Status = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + Type = table.Column(type: "integer", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + Attachments = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TicketActivity", x => x.Id); + table.ForeignKey( + name: "FK_TicketActivity_Tickets_TicketId", + column: x => x.TicketId, + principalTable: "Tickets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_TicketActivity_Users_AssigneeId", + column: x => x.AssigneeId, + principalTable: "Users", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_TicketActivity_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_ApiKeys_OwnerId", + table: "ApiKeys", + column: "OwnerId"); + + migrationBuilder.CreateIndex( + name: "IX_ArchivedChallenges_GameId", + table: "ArchivedChallenges", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_ArchivedChallenges_PlayerId", + table: "ArchivedChallenges", + column: "PlayerId"); + + migrationBuilder.CreateIndex( + name: "IX_ArchivedChallenges_TeamId", + table: "ArchivedChallenges", + column: "TeamId"); + + migrationBuilder.CreateIndex( + name: "IX_ArchivedChallenges_UserId", + table: "ArchivedChallenges", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AwardedChallengeBonuses_ChallengeBonusId", + table: "AwardedChallengeBonuses", + column: "ChallengeBonusId"); + + migrationBuilder.CreateIndex( + name: "IX_AwardedChallengeBonuses_ChallengeId", + table: "AwardedChallengeBonuses", + column: "ChallengeId"); + + migrationBuilder.CreateIndex( + name: "IX_CertificateTemplate_CreatedByUserId", + table: "CertificateTemplate", + column: "CreatedByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeBonuses_ChallengeSpecId", + table: "ChallengeBonuses", + column: "ChallengeSpecId"); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeEvents_ChallengeId", + table: "ChallengeEvents", + column: "ChallengeId"); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeGates_GameId", + table: "ChallengeGates", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_Challenges_GameId", + table: "Challenges", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_Challenges_PlayerId", + table: "Challenges", + column: "PlayerId"); + + migrationBuilder.CreateIndex( + name: "IX_Challenges_SpecId", + table: "Challenges", + column: "SpecId"); + + migrationBuilder.CreateIndex( + name: "IX_Challenges_TeamId", + table: "Challenges", + column: "TeamId"); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeSpecs_GameId", + table: "ChallengeSpecs", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeSpecs_TextSearchVector", + table: "ChallengeSpecs", + column: "TextSearchVector") + .Annotation("Npgsql:IndexMethod", "GIN"); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeSubmissions_ChallengeId", + table: "ChallengeSubmissions", + column: "ChallengeId"); + + migrationBuilder.CreateIndex( + name: "IX_DenormalizedTeamScores_GameId", + table: "DenormalizedTeamScores", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_ExternalGameTeams_GameId", + table: "ExternalGameTeams", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_Feedback_ChallengeId", + table: "Feedback", + column: "ChallengeId"); + + migrationBuilder.CreateIndex( + name: "IX_Feedback_ChallengeSpecId", + table: "Feedback", + column: "ChallengeSpecId"); + + migrationBuilder.CreateIndex( + name: "IX_Feedback_GameId", + table: "Feedback", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_Feedback_PlayerId", + table: "Feedback", + column: "PlayerId"); + + migrationBuilder.CreateIndex( + name: "IX_Feedback_UserId", + table: "Feedback", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_FeedbackSubmissions_ChallengeSpecId", + table: "FeedbackSubmissions", + column: "ChallengeSpecId"); + + migrationBuilder.CreateIndex( + name: "IX_FeedbackSubmissions_FeedbackTemplateId", + table: "FeedbackSubmissions", + column: "FeedbackTemplateId"); + + migrationBuilder.CreateIndex( + name: "IX_FeedbackSubmissions_GameId", + table: "FeedbackSubmissions", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_FeedbackSubmissions_UserId", + table: "FeedbackSubmissions", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_FeedbackTemplates_CreatedByUserId", + table: "FeedbackTemplates", + column: "CreatedByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_GameExportBatches_ExportedByUserId", + table: "GameExportBatches", + column: "ExportedByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_GameGameExportBatch_IncludedGamesId", + table: "GameGameExportBatch", + column: "IncludedGamesId"); + + migrationBuilder.CreateIndex( + name: "IX_Games_CertificateTemplateId", + table: "Games", + column: "CertificateTemplateId"); + + migrationBuilder.CreateIndex( + name: "IX_Games_ChallengesFeedbackTemplateId", + table: "Games", + column: "ChallengesFeedbackTemplateId"); + + migrationBuilder.CreateIndex( + name: "IX_Games_ExternalHostId", + table: "Games", + column: "ExternalHostId"); + + migrationBuilder.CreateIndex( + name: "IX_Games_FeedbackTemplateId", + table: "Games", + column: "FeedbackTemplateId"); + + migrationBuilder.CreateIndex( + name: "IX_Games_PracticeCertificateTemplateId", + table: "Games", + column: "PracticeCertificateTemplateId"); + + migrationBuilder.CreateIndex( + name: "IX_Games_TextSearchVector", + table: "Games", + column: "TextSearchVector") + .Annotation("Npgsql:IndexMethod", "GIN"); + + migrationBuilder.CreateIndex( + name: "IX_ManualBonuses_ChallengeId", + table: "ManualBonuses", + column: "ChallengeId"); + + migrationBuilder.CreateIndex( + name: "IX_ManualBonuses_EnteredByUserId", + table: "ManualBonuses", + column: "EnteredByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_AdvancedFromGameId", + table: "Players", + column: "AdvancedFromGameId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_AdvancedFromPlayerId", + table: "Players", + column: "AdvancedFromPlayerId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_GameId", + table: "Players", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_Id_TeamId", + table: "Players", + columns: new[] { "Id", "TeamId" }); + + migrationBuilder.CreateIndex( + name: "IX_Players_SponsorId", + table: "Players", + column: "SponsorId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_TeamId", + table: "Players", + column: "TeamId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_UserId", + table: "Players", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_UserId_TeamId", + table: "Players", + columns: new[] { "UserId", "TeamId" }); + + migrationBuilder.CreateIndex( + name: "IX_PracticeChallengeGroupChallengeSpec_PracticeChallengeGroupId", + table: "PracticeChallengeGroupChallengeSpec", + column: "PracticeChallengeGroupId"); + + migrationBuilder.CreateIndex( + name: "IX_PracticeChallengeGroups_CreatedByUserId", + table: "PracticeChallengeGroups", + column: "CreatedByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_PracticeChallengeGroups_ParentGroupId", + table: "PracticeChallengeGroups", + column: "ParentGroupId"); + + migrationBuilder.CreateIndex( + name: "IX_PracticeChallengeGroups_TextSearchVector", + table: "PracticeChallengeGroups", + column: "TextSearchVector") + .Annotation("Npgsql:IndexMethod", "GIN"); + + migrationBuilder.CreateIndex( + name: "IX_PracticeChallengeGroups_UpdatedByUserId", + table: "PracticeChallengeGroups", + column: "UpdatedByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_PracticeModeSettings_CertificateTemplateId", + table: "PracticeModeSettings", + column: "CertificateTemplateId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PracticeModeSettings_UpdatedByUserId", + table: "PracticeModeSettings", + column: "UpdatedByUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PublishedCertificate_ChallengeSpecId", + table: "PublishedCertificate", + column: "ChallengeSpecId"); + + migrationBuilder.CreateIndex( + name: "IX_PublishedCertificate_GameId", + table: "PublishedCertificate", + column: "GameId"); + + migrationBuilder.CreateIndex( + name: "IX_PublishedCertificate_OwnerUserId", + table: "PublishedCertificate", + column: "OwnerUserId"); + + migrationBuilder.CreateIndex( + name: "IX_Sponsors_ParentSponsorId", + table: "Sponsors", + column: "ParentSponsorId"); + + migrationBuilder.CreateIndex( + name: "IX_SupportSettings_UpdatedByUserId", + table: "SupportSettings", + column: "UpdatedByUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_SupportSettingsAutoTags_SupportSettingsId", + table: "SupportSettingsAutoTags", + column: "SupportSettingsId"); + + migrationBuilder.CreateIndex( + name: "IX_SystemNotificationInteractions_UserId", + table: "SystemNotificationInteractions", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_SystemNotifications_CreatedByUserId", + table: "SystemNotifications", + column: "CreatedByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_TicketActivity_AssigneeId", + table: "TicketActivity", + column: "AssigneeId"); + + migrationBuilder.CreateIndex( + name: "IX_TicketActivity_TicketId", + table: "TicketActivity", + column: "TicketId"); + + migrationBuilder.CreateIndex( + name: "IX_TicketActivity_UserId", + table: "TicketActivity", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_AssigneeId", + table: "Tickets", + column: "AssigneeId"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_ChallengeId", + table: "Tickets", + column: "ChallengeId"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_CreatorId", + table: "Tickets", + column: "CreatorId"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_Key", + table: "Tickets", + column: "Key", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_PlayerId", + table: "Tickets", + column: "PlayerId"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_RequesterId", + table: "Tickets", + column: "RequesterId"); + + migrationBuilder.CreateIndex( + name: "IX_Users_SponsorId", + table: "Users", + column: "SponsorId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiKeys"); + + migrationBuilder.DropTable( + name: "ArchivedChallenges"); + + migrationBuilder.DropTable( + name: "AwardedChallengeBonuses"); + + migrationBuilder.DropTable( + name: "ChallengeEvents"); + + migrationBuilder.DropTable( + name: "ChallengeGates"); + + migrationBuilder.DropTable( + name: "ChallengeSubmissions"); + + migrationBuilder.DropTable( + name: "DenormalizedTeamScores"); + + migrationBuilder.DropTable( + name: "Extensions"); + + migrationBuilder.DropTable( + name: "ExternalGameTeams"); + + migrationBuilder.DropTable( + name: "Feedback"); + + migrationBuilder.DropTable( + name: "FeedbackSubmissionResponses"); + + migrationBuilder.DropTable( + name: "GameGameExportBatch"); + + migrationBuilder.DropTable( + name: "ManualBonuses"); + + migrationBuilder.DropTable( + name: "PracticeChallengeGroupChallengeSpec"); + + migrationBuilder.DropTable( + name: "PracticeModeSettings"); + + migrationBuilder.DropTable( + name: "PublishedCertificate"); + + migrationBuilder.DropTable( + name: "SupportSettingsAutoTags"); + + migrationBuilder.DropTable( + name: "SystemNotificationInteractions"); + + migrationBuilder.DropTable( + name: "TicketActivity"); + + migrationBuilder.DropTable( + name: "ChallengeBonuses"); + + migrationBuilder.DropTable( + name: "FeedbackSubmissions"); + + migrationBuilder.DropTable( + name: "GameExportBatches"); + + migrationBuilder.DropTable( + name: "PracticeChallengeGroups"); + + migrationBuilder.DropTable( + name: "SupportSettings"); + + migrationBuilder.DropTable( + name: "SystemNotifications"); + + migrationBuilder.DropTable( + name: "Tickets"); + + migrationBuilder.DropTable( + name: "Challenges"); + + migrationBuilder.DropTable( + name: "ChallengeSpecs"); + + migrationBuilder.DropTable( + name: "Players"); + + migrationBuilder.DropTable( + name: "Games"); + + migrationBuilder.DropTable( + name: "CertificateTemplate"); + + migrationBuilder.DropTable( + name: "ExternalGameHosts"); + + migrationBuilder.DropTable( + name: "FeedbackTemplates"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Sponsors"); + } + } +} diff --git a/src/Gameboard.Api/Migrations/GameboardDbContextModelSnapshot.cs b/src/Gameboard.Api/Migrations/GameboardDbContextModelSnapshot.cs new file mode 100644 index 00000000..bf762085 --- /dev/null +++ b/src/Gameboard.Api/Migrations/GameboardDbContextModelSnapshot.cs @@ -0,0 +1,2458 @@ +// Copyright 2025 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. + +// +using System; +using Gameboard.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Gameboard.Api.Migrations +{ + [DbContext(typeof(GameboardDbContext))] + partial class GameboardDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("GameGameExportBatch", b => + { + b.Property("ExportedInBatchesId") + .HasColumnType("text"); + + b.Property("IncludedGamesId") + .HasColumnType("character varying(40)"); + + b.HasKey("ExportedInBatchesId", "IncludedGamesId"); + + b.HasIndex("IncludedGamesId"); + + b.ToTable("GameGameExportBatch"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ApiKey", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ExpiresOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NULL"); + + b.Property("GeneratedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnerId") + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ArchivedChallenge", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Duration") + .HasColumnType("bigint"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Events") + .HasColumnType("text"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasGamespaceDeployed") + .HasColumnType("boolean"); + + b.Property("LastScoreTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSyncTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayerMode") + .HasColumnType("integer"); + + b.Property("PlayerName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Result") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("State") + .HasColumnType("text"); + + b.Property("Submissions") + .HasColumnType("text"); + + b.Property("Tag") + .HasColumnType("text"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TeamMembers") + .HasColumnType("text"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.ToTable("ArchivedChallenges"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.AwardedChallengeBonus", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeBonusId") + .HasColumnType("character varying(40)"); + + b.Property("ChallengeId") + .HasColumnType("character varying(40)"); + + b.Property("EnteredOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("InternalSummary") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeBonusId"); + + b.HasIndex("ChallengeId"); + + b.ToTable("AwardedChallengeBonuses"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.CertificateTemplate", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.ToTable("CertificateTemplate"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Challenge", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameEngineType") + .HasColumnType("integer"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GraderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasDeployedGamespace") + .HasColumnType("boolean"); + + b.Property("LastScoreTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSyncTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PendingSubmission") + .HasColumnType("text"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayerMode") + .HasColumnType("integer"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("double precision"); + + b.Property("SpecId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("State") + .HasColumnType("text"); + + b.Property("Tag") + .HasColumnType("text"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("WhenCreated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("SpecId"); + + b.HasIndex("TeamId"); + + b.ToTable("Challenges"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonus", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeBonusType") + .HasColumnType("integer"); + + b.Property("ChallengeSpecId") + .HasColumnType("character varying(40)"); + + b.Property("Description") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PointValue") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeSpecId"); + + b.ToTable("ChallengeBonuses"); + + b.HasDiscriminator("ChallengeBonusType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeEvent", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Text") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeId"); + + b.ToTable("ChallengeEvents"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeGate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("RequiredId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("RequiredScore") + .HasColumnType("double precision"); + + b.Property("TargetId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.ToTable("ChallengeGates"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSpec", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AverageDeploySeconds") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExternalId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameEngineType") + .HasColumnType("integer"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("IsHidden") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("R") + .HasColumnType("real"); + + b.Property("ShowSolutionGuideInCompetitiveMode") + .HasColumnType("boolean"); + + b.Property("SolutionGuideUrl") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Tag") + .HasColumnType("text"); + + b.Property("Tags") + .HasColumnType("text"); + + b.Property("Text") + .HasColumnType("text"); + + b.Property("TextSearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Name", "Id", "Description", "GameId", "Tag", "Tags", "Text" }); + + b.Property("X") + .HasColumnType("real"); + + b.Property("Y") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("TextSearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("TextSearchVector"), "GIN"); + + b.ToTable("ChallengeSpecs"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSubmission", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Answers") + .IsRequired() + .HasColumnType("text"); + + b.Property("ChallengeId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Score") + .ValueGeneratedOnAdd() + .HasColumnType("double precision") + .HasDefaultValue(0.0); + + b.Property("SubmittedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeId"); + + b.ToTable("ChallengeSubmissions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.DenormalizedTeamScore", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("CumulativeTimeMs") + .HasColumnType("double precision"); + + b.Property("GameId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("ScoreAdvanced") + .HasColumnType("double precision"); + + b.Property("ScoreAutoBonus") + .HasColumnType("double precision"); + + b.Property("ScoreChallenge") + .HasColumnType("double precision"); + + b.Property("ScoreManualBonus") + .HasColumnType("double precision"); + + b.Property("ScoreOverall") + .HasColumnType("double precision"); + + b.Property("SolveCountComplete") + .HasColumnType("integer"); + + b.Property("SolveCountNone") + .HasColumnType("integer"); + + b.Property("SolveCountPartial") + .HasColumnType("integer"); + + b.Property("TeamId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TeamName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.ToTable("DenormalizedTeamScores"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Extension", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("HostUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasAlternateKey("Type"); + + b.ToTable("Extensions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameHost", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ClientUrl") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DestroyResourcesOnDeployFailure") + .HasColumnType("boolean"); + + b.Property("GamespaceDeployBatchSize") + .HasColumnType("integer"); + + b.Property("HostApiKey") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("HostUrl") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("HttpTimeoutInSeconds") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PingEndpoint") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("StartupEndpoint") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("TeamExtendedEndpoint") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.ToTable("ExternalGameHosts"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameTeam", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("DeployStatus") + .HasColumnType("integer"); + + b.Property("ExternalGameUrl") + .HasColumnType("text"); + + b.Property("GameId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("TeamId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasAlternateKey("TeamId", "GameId"); + + b.HasIndex("GameId"); + + b.ToTable("ExternalGameTeams"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Feedback", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Answers") + .HasColumnType("text"); + + b.Property("ChallengeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ChallengeSpecId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Submitted") + .HasColumnType("boolean"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeId"); + + b.HasIndex("ChallengeSpecId"); + + b.HasIndex("GameId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("UserId"); + + b.ToTable("Feedback"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmission", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AttachedEntityType") + .HasColumnType("integer"); + + b.Property("FeedbackTemplateId") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("WhenCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("WhenEdited") + .HasColumnType("timestamp with time zone"); + + b.Property("WhenFinalized") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackTemplateId"); + + b.HasIndex("UserId"); + + b.ToTable("FeedbackSubmissions"); + + b.HasDiscriminator("AttachedEntityType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackTemplate", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("HelpText") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.ToTable("FeedbackTemplates"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Game", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AllowLateStart") + .HasColumnType("boolean"); + + b.Property("AllowPreview") + .HasColumnType("boolean"); + + b.Property("AllowPublicScoreboardAccess") + .HasColumnType("boolean"); + + b.Property("AllowReset") + .HasColumnType("boolean"); + + b.Property("Background") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CardText1") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CardText2") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CardText3") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CertificateTemplateId") + .HasColumnType("text"); + + b.Property("ChallengesFeedbackTemplateId") + .HasColumnType("text"); + + b.Property("Competition") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Division") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ExternalHostId") + .HasColumnType("character varying(40)"); + + b.Property("FeedbackConfig") + .HasColumnType("text"); + + b.Property("FeedbackTemplateId") + .HasColumnType("text"); + + b.Property("GameEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("GameMarkdown") + .HasColumnType("text"); + + b.Property("GameStart") + .HasColumnType("timestamp with time zone"); + + b.Property("GamespaceLimitPerSession") + .HasColumnType("integer"); + + b.Property("IsFeatured") + .HasColumnType("boolean"); + + b.Property("IsPublished") + .HasColumnType("boolean"); + + b.Property("Logo") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("MaxAttempts") + .HasColumnType("integer"); + + b.Property("MaxTeamSize") + .HasColumnType("integer"); + + b.Property("MinTeamSize") + .HasColumnType("integer"); + + b.Property("Mode") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PlayerMode") + .HasColumnType("integer"); + + b.Property("PracticeCertificateTemplateId") + .HasColumnType("text"); + + b.Property("RegistrationClose") + .HasColumnType("timestamp with time zone"); + + b.Property("RegistrationConstraint") + .HasColumnType("text"); + + b.Property("RegistrationMarkdown") + .HasColumnType("text"); + + b.Property("RegistrationOpen") + .HasColumnType("timestamp with time zone"); + + b.Property("RegistrationType") + .HasColumnType("integer"); + + b.Property("RequireSponsoredTeam") + .HasColumnType("boolean"); + + b.Property("RequireSynchronizedStart") + .HasColumnType("boolean"); + + b.Property("Season") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("SessionAvailabilityWarningThreshold") + .HasColumnType("integer"); + + b.Property("SessionLimit") + .HasColumnType("integer"); + + b.Property("SessionMinutes") + .HasColumnType("integer"); + + b.Property("ShowOnHomePageInPracticeMode") + .HasColumnType("boolean"); + + b.Property("Sponsor") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TextSearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Name", "Competition", "Id", "Track", "Season", "Division" }); + + b.Property("Track") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CertificateTemplateId"); + + b.HasIndex("ChallengesFeedbackTemplateId"); + + b.HasIndex("ExternalHostId"); + + b.HasIndex("FeedbackTemplateId"); + + b.HasIndex("PracticeCertificateTemplateId"); + + b.HasIndex("TextSearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("TextSearchVector"), "GIN"); + + b.ToTable("Games"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.GameExportBatch", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ExportedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("ExportedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ExportedByUserId"); + + b.ToTable("GameExportBatches", (string)null); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualBonus", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EnteredByUserId") + .HasColumnType("character varying(40)"); + + b.Property("EnteredOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PointValue") + .HasColumnType("double precision"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EnteredByUserId"); + + b.ToTable("ManualBonuses"); + + b.HasDiscriminator("Type"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Player", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Advanced") + .HasColumnType("boolean"); + + b.Property("AdvancedFromGameId") + .HasColumnType("character varying(40)"); + + b.Property("AdvancedFromPlayerId") + .HasColumnType("character varying(40)"); + + b.Property("AdvancedFromTeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AdvancedWithScore") + .HasColumnType("double precision"); + + b.Property("ApprovedName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CorrectCount") + .HasColumnType("integer"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("InviteCode") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("IsLateStart") + .HasColumnType("boolean"); + + b.Property("IsReady") + .HasColumnType("boolean"); + + b.Property("Mode") + .HasColumnType("integer"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NameStatus") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PartialCount") + .HasColumnType("integer"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("SessionBegin") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionMinutes") + .HasColumnType("double precision"); + + b.Property("SponsorId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Time") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("WhenCreated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AdvancedFromGameId"); + + b.HasIndex("AdvancedFromPlayerId"); + + b.HasIndex("GameId"); + + b.HasIndex("SponsorId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.HasIndex("Id", "TeamId"); + + b.HasIndex("UserId", "TeamId"); + + b.ToTable("Players"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroup", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsFeatured") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("ParentGroupId") + .HasColumnType("text"); + + b.Property("TextSearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Name", "Id", "Description" }); + + b.Property("UpdatedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("ParentGroupId"); + + b.HasIndex("TextSearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("TextSearchVector"), "GIN"); + + b.HasIndex("UpdatedByUserId"); + + b.ToTable("PracticeChallengeGroups"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroupChallengeSpec", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ChallengeSpecId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("PracticeChallengeGroupId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasAlternateKey("ChallengeSpecId", "PracticeChallengeGroupId"); + + b.HasIndex("PracticeChallengeGroupId"); + + b.ToTable("PracticeChallengeGroupChallengeSpec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeModeSettings", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AttemptLimit") + .HasColumnType("integer"); + + b.Property("CertificateTemplateId") + .HasColumnType("text"); + + b.Property("DefaultPracticeSessionLengthMinutes") + .HasColumnType("integer"); + + b.Property("IntroTextMarkdown") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("MaxConcurrentPracticeSessions") + .HasColumnType("integer"); + + b.Property("MaxPracticeSessionLengthMinutes") + .HasColumnType("integer"); + + b.Property("SuggestedSearches") + .HasColumnType("text"); + + b.Property("UpdatedByUserId") + .HasColumnType("character varying(40)"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CertificateTemplateId") + .IsUnique(); + + b.HasIndex("UpdatedByUserId") + .IsUnique(); + + b.ToTable("PracticeModeSettings"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedCertificate", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Mode") + .HasColumnType("integer"); + + b.Property("OwnerUserId") + .HasColumnType("character varying(40)"); + + b.Property("PublishedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("PublishedCertificate"); + + b.HasDiscriminator("Mode"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Sponsor", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("Logo") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentSponsorId") + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("ParentSponsorId"); + + b.ToTable("Sponsors"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettings", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("SupportPageGreeting") + .HasColumnType("text"); + + b.Property("UpdatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("UpdatedByUserId") + .IsUnique(); + + b.ToTable("SupportSettings"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettingsAutoTag", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConditionType") + .HasColumnType("integer"); + + b.Property("ConditionValue") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("SupportSettingsId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Tag") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("SupportSettingsId"); + + b.ToTable("SupportSettingsAutoTags"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotification", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("EndsOn") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDismissible") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownContent") + .IsRequired() + .HasColumnType("text"); + + b.Property("NotificationType") + .HasColumnType("integer"); + + b.Property("StartsOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.ToTable("SystemNotifications"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotificationInteraction", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DismissedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("SawCalloutOn") + .HasColumnType("timestamp with time zone"); + + b.Property("SawFullNotificationOn") + .HasColumnType("timestamp with time zone"); + + b.Property("SystemNotificationId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasAlternateKey("SystemNotificationId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("SystemNotificationInteractions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Ticket", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("AssigneeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("ChallengeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Key") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Key")); + + b.Property("Label") + .HasColumnType("text"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("RequesterId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("StaffCreated") + .HasColumnType("boolean"); + + b.Property("Status") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TeamId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("AssigneeId"); + + b.HasIndex("ChallengeId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("Key") + .IsUnique(); + + b.HasIndex("PlayerId"); + + b.HasIndex("RequesterId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.TicketActivity", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AssigneeId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TicketId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasKey("Id"); + + b.HasIndex("AssigneeId"); + + b.HasIndex("TicketId"); + + b.HasIndex("UserId"); + + b.ToTable("TicketActivity"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.User", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("ApprovedName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasDefaultSponsor") + .HasColumnType("boolean"); + + b.Property("LastIdpAssignedRole") + .HasColumnType("integer"); + + b.Property("LastLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LoginCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NameStatus") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("PlayAudioOnBrowserNotification") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("SponsorId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.Property("Username") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("SponsorId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonusCompleteSolveRank", b => + { + b.HasBaseType("Gameboard.Api.Data.ChallengeBonus"); + + b.Property("SolveRank") + .HasColumnType("integer"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionChallengeSpec", b => + { + b.HasBaseType("Gameboard.Api.Data.FeedbackSubmission"); + + b.Property("ChallengeSpecId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasIndex("ChallengeSpecId"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionGame", b => + { + b.HasBaseType("Gameboard.Api.Data.FeedbackSubmission"); + + b.Property("GameId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasIndex("GameId"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualChallengeBonus", b => + { + b.HasBaseType("Gameboard.Api.Data.ManualBonus"); + + b.Property("ChallengeId") + .IsRequired() + .HasColumnType("character varying(40)"); + + b.HasIndex("ChallengeId"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualTeamBonus", b => + { + b.HasBaseType("Gameboard.Api.Data.ManualBonus"); + + b.Property("TeamId") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedCompetitiveCertificate", b => + { + b.HasBaseType("Gameboard.Api.Data.PublishedCertificate"); + + b.Property("GameId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasIndex("GameId"); + + b.HasIndex("OwnerUserId"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedPracticeCertificate", b => + { + b.HasBaseType("Gameboard.Api.Data.PublishedCertificate"); + + b.Property("ChallengeSpecId") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.HasIndex("ChallengeSpecId"); + + b.HasIndex("OwnerUserId"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("GameGameExportBatch", b => + { + b.HasOne("Gameboard.Api.Data.GameExportBatch", null) + .WithMany() + .HasForeignKey("ExportedInBatchesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.Game", null) + .WithMany() + .HasForeignKey("IncludedGamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ApiKey", b => + { + b.HasOne("Gameboard.Api.Data.User", "Owner") + .WithMany("ApiKeys") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.AwardedChallengeBonus", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeBonus", "ChallengeBonus") + .WithMany("AwardedTo") + .HasForeignKey("ChallengeBonusId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("AwardedBonuses") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Challenge"); + + b.Navigation("ChallengeBonus"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.CertificateTemplate", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedCertificateTemplates") + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Challenge", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Challenges") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.Player", "Player") + .WithMany("Challenges") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "Spec") + .WithMany("Challenges") + .HasForeignKey("SpecId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Game"); + + b.Navigation("Player"); + + b.Navigation("Spec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonus", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("Bonuses") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ChallengeSpec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeEvent", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Events") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeGate", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Prerequisites") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSpec", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Specs") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSubmission", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Submissions") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.DenormalizedTeamScore", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("DenormalizedTeamScores") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameTeam", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("ExternalGameTeams") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Feedback", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Feedback") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("Feedback") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Feedback") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.Player", "Player") + .WithMany("Feedback") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("Feedback") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Challenge"); + + b.Navigation("ChallengeSpec"); + + b.Navigation("Game"); + + b.Navigation("Player"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmission", b => + { + b.HasOne("Gameboard.Api.Data.FeedbackTemplate", "FeedbackTemplate") + .WithMany("Submissions") + .HasForeignKey("FeedbackTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("FeedbackSubmissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsMany("Gameboard.Api.Features.Feedback.QuestionSubmission", "Responses", b1 => + { + b1.Property("FeedbackSubmissionId") + .HasColumnType("text"); + + b1.Property("Id") + .HasColumnType("text"); + + b1.Property("Answer") + .HasColumnType("text"); + + b1.Property("Prompt") + .HasColumnType("text"); + + b1.Property("ShortName") + .HasColumnType("text"); + + b1.HasKey("FeedbackSubmissionId", "Id"); + + b1.ToTable("FeedbackSubmissionResponses", (string)null); + + b1.WithOwner() + .HasForeignKey("FeedbackSubmissionId"); + }); + + b.Navigation("FeedbackTemplate"); + + b.Navigation("Responses"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackTemplate", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedFeedbackTemplates") + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Game", b => + { + b.HasOne("Gameboard.Api.Data.CertificateTemplate", "CertificateTemplate") + .WithMany("UseAsTemplateForGames") + .HasForeignKey("CertificateTemplateId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.FeedbackTemplate", "ChallengesFeedbackTemplate") + .WithMany("UseAsFeedbackTemplateForGames") + .HasForeignKey("ChallengesFeedbackTemplateId"); + + b.HasOne("Gameboard.Api.Data.ExternalGameHost", "ExternalHost") + .WithMany("UsedByGames") + .HasForeignKey("ExternalHostId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.FeedbackTemplate", "FeedbackTemplate") + .WithMany("UseAsFeedbackTemplateForGameChallenges") + .HasForeignKey("FeedbackTemplateId"); + + b.HasOne("Gameboard.Api.Data.CertificateTemplate", "PracticeCertificateTemplate") + .WithMany("UseAsPracticeTemplateForGames") + .HasForeignKey("PracticeCertificateTemplateId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CertificateTemplate"); + + b.Navigation("ChallengesFeedbackTemplate"); + + b.Navigation("ExternalHost"); + + b.Navigation("FeedbackTemplate"); + + b.Navigation("PracticeCertificateTemplate"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.GameExportBatch", b => + { + b.HasOne("Gameboard.Api.Data.User", "ExportedByUser") + .WithMany("GameExportBatches") + .HasForeignKey("ExportedByUserId"); + + b.Navigation("ExportedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualBonus", b => + { + b.HasOne("Gameboard.Api.Data.User", "EnteredByUser") + .WithMany("EnteredManualBonuses") + .HasForeignKey("EnteredByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EnteredByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Player", b => + { + b.HasOne("Gameboard.Api.Data.Game", "AdvancedFromGame") + .WithMany("AdvancedPlayers") + .HasForeignKey("AdvancedFromGameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.Player", "AdvancedFromPlayer") + .WithMany("AdvancedToPlayers") + .HasForeignKey("AdvancedFromPlayerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("Players") + .HasForeignKey("GameId"); + + b.HasOne("Gameboard.Api.Data.Sponsor", "Sponsor") + .WithMany("SponsoredPlayers") + .HasForeignKey("SponsorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("Enrollments") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("AdvancedFromGame"); + + b.Navigation("AdvancedFromPlayer"); + + b.Navigation("Game"); + + b.Navigation("Sponsor"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroup", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedPracticeChallengeGroups") + .HasForeignKey("CreatedByUserId"); + + b.HasOne("Gameboard.Api.Data.PracticeChallengeGroup", "ParentGroup") + .WithMany("ChildGroups") + .HasForeignKey("ParentGroupId"); + + b.HasOne("Gameboard.Api.Data.User", "UpdatedByUser") + .WithMany("UpdatedPracticeChallengeGroups") + .HasForeignKey("UpdatedByUserId"); + + b.Navigation("CreatedByUser"); + + b.Navigation("ParentGroup"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroupChallengeSpec", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("PracticeChallengeGroups") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.PracticeChallengeGroup", "PracticeChallengeGroup") + .WithMany("ChallengeSpecs") + .HasForeignKey("PracticeChallengeGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeSpec"); + + b.Navigation("PracticeChallengeGroup"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeModeSettings", b => + { + b.HasOne("Gameboard.Api.Data.CertificateTemplate", "CertificateTemplate") + .WithOne("UsedAsPracticeModeDefault") + .HasForeignKey("Gameboard.Api.Data.PracticeModeSettings", "CertificateTemplateId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.User", "UpdatedByUser") + .WithOne("UpdatedPracticeModeSettings") + .HasForeignKey("Gameboard.Api.Data.PracticeModeSettings", "UpdatedByUserId"); + + b.Navigation("CertificateTemplate"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Sponsor", b => + { + b.HasOne("Gameboard.Api.Data.Sponsor", "ParentSponsor") + .WithMany("ChildSponsors") + .HasForeignKey("ParentSponsorId"); + + b.Navigation("ParentSponsor"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettings", b => + { + b.HasOne("Gameboard.Api.Data.User", "UpdatedByUser") + .WithOne("UpdatedSupportSettings") + .HasForeignKey("Gameboard.Api.Data.SupportSettings", "UpdatedByUserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettingsAutoTag", b => + { + b.HasOne("Gameboard.Api.Data.SupportSettings", "SupportSettings") + .WithMany("AutoTags") + .HasForeignKey("SupportSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SupportSettings"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotification", b => + { + b.HasOne("Gameboard.Api.Data.User", "CreatedByUser") + .WithMany("CreatedSystemNotifications") + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotificationInteraction", b => + { + b.HasOne("Gameboard.Api.Data.SystemNotification", "SystemNotification") + .WithMany("Interactions") + .HasForeignKey("SystemNotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany("SystemNotificationInteractions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SystemNotification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Ticket", b => + { + b.HasOne("Gameboard.Api.Data.User", "Assignee") + .WithMany() + .HasForeignKey("AssigneeId"); + + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("Tickets") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.HasOne("Gameboard.Api.Data.Player", "Player") + .WithMany("Tickets") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Gameboard.Api.Data.User", "Requester") + .WithMany() + .HasForeignKey("RequesterId"); + + b.Navigation("Assignee"); + + b.Navigation("Challenge"); + + b.Navigation("Creator"); + + b.Navigation("Player"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.TicketActivity", b => + { + b.HasOne("Gameboard.Api.Data.User", "Assignee") + .WithMany() + .HasForeignKey("AssigneeId"); + + b.HasOne("Gameboard.Api.Data.Ticket", "Ticket") + .WithMany("Activity") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Gameboard.Api.Data.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Assignee"); + + b.Navigation("Ticket"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.User", b => + { + b.HasOne("Gameboard.Api.Data.Sponsor", "Sponsor") + .WithMany("SponsoredUsers") + .HasForeignKey("SponsorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sponsor"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionChallengeSpec", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("FeedbackSubmissions") + .HasForeignKey("ChallengeSpecId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeSpec"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackSubmissionGame", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("FeedbackSubmissions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ManualChallengeBonus", b => + { + b.HasOne("Gameboard.Api.Data.Challenge", "Challenge") + .WithMany("AwardedManualBonuses") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedCompetitiveCertificate", b => + { + b.HasOne("Gameboard.Api.Data.Game", "Game") + .WithMany("PublishedCompetitiveCertificates") + .HasForeignKey("GameId"); + + b.HasOne("Gameboard.Api.Data.User", "OwnerUser") + .WithMany("PublishedCompetitiveCertificates") + .HasForeignKey("OwnerUserId") + .HasConstraintName("FK_OwnerUserId_Users_Id"); + + b.Navigation("Game"); + + b.Navigation("OwnerUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PublishedPracticeCertificate", b => + { + b.HasOne("Gameboard.Api.Data.ChallengeSpec", "ChallengeSpec") + .WithMany("PublishedPracticeCertificates") + .HasForeignKey("ChallengeSpecId"); + + b.HasOne("Gameboard.Api.Data.User", "OwnerUser") + .WithMany("PublishedPracticeCertificates") + .HasForeignKey("OwnerUserId") + .HasConstraintName("FK_OwnerUserId_Users_Id"); + + b.Navigation("ChallengeSpec"); + + b.Navigation("OwnerUser"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.CertificateTemplate", b => + { + b.Navigation("UseAsPracticeTemplateForGames"); + + b.Navigation("UseAsTemplateForGames"); + + b.Navigation("UsedAsPracticeModeDefault"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Challenge", b => + { + b.Navigation("AwardedBonuses"); + + b.Navigation("AwardedManualBonuses"); + + b.Navigation("Events"); + + b.Navigation("Feedback"); + + b.Navigation("Submissions"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeBonus", b => + { + b.Navigation("AwardedTo"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ChallengeSpec", b => + { + b.Navigation("Bonuses"); + + b.Navigation("Challenges"); + + b.Navigation("Feedback"); + + b.Navigation("FeedbackSubmissions"); + + b.Navigation("PracticeChallengeGroups"); + + b.Navigation("PublishedPracticeCertificates"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.ExternalGameHost", b => + { + b.Navigation("UsedByGames"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.FeedbackTemplate", b => + { + b.Navigation("Submissions"); + + b.Navigation("UseAsFeedbackTemplateForGameChallenges"); + + b.Navigation("UseAsFeedbackTemplateForGames"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Game", b => + { + b.Navigation("AdvancedPlayers"); + + b.Navigation("Challenges"); + + b.Navigation("DenormalizedTeamScores"); + + b.Navigation("ExternalGameTeams"); + + b.Navigation("Feedback"); + + b.Navigation("FeedbackSubmissions"); + + b.Navigation("Players"); + + b.Navigation("Prerequisites"); + + b.Navigation("PublishedCompetitiveCertificates"); + + b.Navigation("Specs"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Player", b => + { + b.Navigation("AdvancedToPlayers"); + + b.Navigation("Challenges"); + + b.Navigation("Feedback"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.PracticeChallengeGroup", b => + { + b.Navigation("ChallengeSpecs"); + + b.Navigation("ChildGroups"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Sponsor", b => + { + b.Navigation("ChildSponsors"); + + b.Navigation("SponsoredPlayers"); + + b.Navigation("SponsoredUsers"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SupportSettings", b => + { + b.Navigation("AutoTags"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.SystemNotification", b => + { + b.Navigation("Interactions"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.Ticket", b => + { + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Gameboard.Api.Data.User", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("CreatedCertificateTemplates"); + + b.Navigation("CreatedFeedbackTemplates"); + + b.Navigation("CreatedPracticeChallengeGroups"); + + b.Navigation("CreatedSystemNotifications"); + + b.Navigation("Enrollments"); + + b.Navigation("EnteredManualBonuses"); + + b.Navigation("Feedback"); + + b.Navigation("FeedbackSubmissions"); + + b.Navigation("GameExportBatches"); + + b.Navigation("PublishedCompetitiveCertificates"); + + b.Navigation("PublishedPracticeCertificates"); + + b.Navigation("SystemNotificationInteractions"); + + b.Navigation("UpdatedPracticeChallengeGroups"); + + b.Navigation("UpdatedPracticeModeSettings"); + + b.Navigation("UpdatedSupportSettings"); + }); +#pragma warning restore 612, 618 + } + } +}