diff --git a/TASVideos.Api/Extensions/EntityExtensions.cs b/TASVideos.Api/Extensions/EntityExtensions.cs index ad86e30fd..58b095eb9 100644 --- a/TASVideos.Api/Extensions/EntityExtensions.cs +++ b/TASVideos.Api/Extensions/EntityExtensions.cs @@ -33,7 +33,9 @@ public static IQueryable ToPublicationsResponse(this IQuer { Id = p.Id, Title = p.Title, - Branch = p.Branch, + Branch = p.GameGoal!.DisplayName, + Goal = p.GameGoal!.DisplayName, + GameGoalId = p.GameGoalId ?? -1, EmulatorVersion = p.EmulatorVersion, Class = p.PublicationClass!.Name, SystemCode = p.System!.Code, @@ -70,6 +72,9 @@ public static IQueryable ToSubmissionsResponse(this IQuerya ? s.Publication.Id : null, Title = s.Title, + Goal = s.GameGoal != null + ? s.GameGoal.DisplayName + : null, IntendedClass = s.IntendedClass != null ? s.IntendedClass.Name : null, diff --git a/TASVideos.Api/Responses/PublicationsResponse.cs b/TASVideos.Api/Responses/PublicationsResponse.cs index d74dd9488..3bae9ebe0 100644 --- a/TASVideos.Api/Responses/PublicationsResponse.cs +++ b/TASVideos.Api/Responses/PublicationsResponse.cs @@ -12,8 +12,11 @@ public class PublicationsResponse [Sortable] public string Title { get; init; } = ""; + // Left in for backwards compatibility [Sortable] public string? Branch { get; init; } = ""; + public string Goal { get; init; } = ""; + public int GameGoalId { get; init; } [Sortable] public string? EmulatorVersion { get; init; } = ""; diff --git a/TASVideos.Api/Responses/SubmissionsResponse.cs b/TASVideos.Api/Responses/SubmissionsResponse.cs index 7d8fee8bd..c8556791a 100644 --- a/TASVideos.Api/Responses/SubmissionsResponse.cs +++ b/TASVideos.Api/Responses/SubmissionsResponse.cs @@ -57,6 +57,7 @@ public class SubmissionsResponse [Sortable] public string? Branch { get; init; } + public string? Goal { get; init; } [Sortable] public string? RomName { get; init; } diff --git a/TASVideos.Core/Dtos/PublicationHistoryNode.cs b/TASVideos.Core/Dtos/PublicationHistoryNode.cs index 6e3091c8a..c75540f74 100644 --- a/TASVideos.Core/Dtos/PublicationHistoryNode.cs +++ b/TASVideos.Core/Dtos/PublicationHistoryNode.cs @@ -4,14 +4,14 @@ public class PublicationHistoryGroup { public int GameId { get; init; } - public IEnumerable Branches { get; init; } = new List(); + public IEnumerable Goals { get; init; } = new List(); } public class PublicationHistoryNode { public int Id { get; init; } public string Title { get; init; } = ""; - public string? Branch { get; init; } + public string? Goal { get; init; } public DateTime CreateTimestamp { get; set; } public string? ClassIconPath { get; set; } diff --git a/TASVideos.Core/Services/PublicationHistory.cs b/TASVideos.Core/Services/PublicationHistory.cs index 9adf2203c..dded33a60 100644 --- a/TASVideos.Core/Services/PublicationHistory.cs +++ b/TASVideos.Core/Services/PublicationHistory.cs @@ -34,7 +34,7 @@ public PublicationHistory(ApplicationDbContext db) { Id = p.Id, Title = p.Title, - Branch = p.Branch, + Goal = p.GameGoal!.DisplayName, CreateTimestamp = p.CreateTimestamp, ObsoletedById = p.ObsoletedById, ClassIconPath = p.PublicationClass!.IconPath, @@ -54,7 +54,7 @@ public PublicationHistory(ApplicationDbContext db) return new PublicationHistoryGroup { GameId = gameId, - Branches = publications + Goals = publications .Where(p => !p.ObsoletedById.HasValue) .ToList() }; diff --git a/TASVideos.Data/ApplicationDbContext.cs b/TASVideos.Data/ApplicationDbContext.cs index d38e9c3e1..32ebbfd84 100644 --- a/TASVideos.Data/ApplicationDbContext.cs +++ b/TASVideos.Data/ApplicationDbContext.cs @@ -57,6 +57,7 @@ public ApplicationDbContext(DbContextOptions options, IHtt public DbSet GameVersions { get; set; } = null!; public DbSet GameGroups { get; set; } = null!; public DbSet GameGameGroups { get; set; } = null!; + public DbSet GameGoals { get; set; } = null!; // Forum tables public DbSet ForumCategories { get; set; } = null!; diff --git a/TASVideos.Data/CustomDataMigrations/goal-migration.sql b/TASVideos.Data/CustomDataMigrations/goal-migration.sql new file mode 100644 index 000000000..2923859bf --- /dev/null +++ b/TASVideos.Data/CustomDataMigrations/goal-migration.sql @@ -0,0 +1,84 @@ +-- Does the initial migration of branches into game goals +DO $$ +DECLARE + sub record; + pub record; + tempBranch citext; + tempGameGoal record; + tempGoalId integer; +BEGIN + + RAISE NOTICE '--------------------------'; + RAISE NOTICE '---- Submission Goals ----'; + RAISE NOTICE '--------------------------'; + DROP TABLE IF EXISTS _submissions; + CREATE TEMPORARY TABLE _submissions (id int primary key, game_id int, branch citext); + INSERT INTO _submissions + SELECT id, game_id, TRIM(REPLACE(branch, '"', '')) AS branch + FROM submissions + WHERE game_goal_id IS NULL + AND game_id IS NOT NULL + ORDER BY id; + + FOR sub in SELECT id, game_id, branch FROM _submissions LOOP + --RAISE NOTICE '-- Submission % --', sub.id; + tempBranch = sub.branch; + + -- Handle Goal Record + IF sub.branch IS NULL THEN + tempBranch = 'baseline'; + END IF; + + SELECT game_id, id INTO tempGameGoal FROM game_goals WHERE game_id = sub.game_id AND display_name = tempBranch; + IF tempGameGoal IS NULL THEN + --RAISE NOTICE 'GameGoal does not already exist, INSERTING'; + INSERT INTO game_goals (game_id, display_name) VALUES (sub.game_id, tempBranch); + SELECT game_id, display_name, id INTO tempGameGoal FROM game_goals WHERE game_id = sub.game_id AND display_name = tempBranch; + IF tempGameGoal IS NULL THEN + RAISE EXCEPTION 'FAILED TO INSERT GAME GOAL FOR %', sub.branch; + END IF; + END IF; + + UPDATE submissions SET game_goal_id = tempGameGoal.id WHERE id = sub.id; + END LOOP; + + RAISE NOTICE '--------------------------'; + RAISE NOTICE '---- Publication Goals ----'; + RAISE NOTICE '--------------------------'; + DROP TABLE IF EXISTS _publications; + CREATE TEMPORARY TABLE _publications (id int primary key, game_id int, branch citext); + INSERT INTO _publications + SELECT id, game_id, TRIM(REPLACE(branch, '"', '')) AS branch + FROM publications + WHERE game_goal_id IS NULL + AND game_id IS NOT NULL + ORDER BY id; + + FOR pub in SELECT id, game_id, branch FROM _publications LOOP + --RAISE NOTICE '-- Submission % --', sub.id; + tempBranch = pub.branch; + + -- Handle Goal Record + IF pub.branch IS NULL THEN + tempBranch = 'baseline'; + END IF; + + SELECT game_id, id INTO tempGameGoal FROM game_goals WHERE game_id = pub.game_id AND display_name = tempBranch; + IF tempGameGoal IS NULL THEN + --RAISE NOTICE 'GameGoal does not already exist, INSERTING'; + INSERT INTO game_goals (game_id, display_name) VALUES (pub.game_id, tempBranch); + SELECT game_id, display_name, id INTO tempGameGoal FROM game_goals WHERE game_id = pub.game_id AND display_name = tempBranch; + IF tempGameGoal IS NULL THEN + RAISE EXCEPTION 'FAILED TO INSERT GAME GOAL FOR %', pub.branch; + END IF; + END IF; + + UPDATE publications SET game_goal_id = tempGameGoal.id WHERE id = pub.id; + END LOOP; +END $$ + +--SELECT * FROM game_goals +--UPDATE submissions SET game_goal_id = NULL +--UPDATE publications SET game_goal_id = NULL +--DELETE FROM game_goals + diff --git a/TASVideos.Data/Entity/Game/Game.cs b/TASVideos.Data/Entity/Game/Game.cs index b0f0f36a4..03117c2d9 100644 --- a/TASVideos.Data/Entity/Game/Game.cs +++ b/TASVideos.Data/Entity/Game/Game.cs @@ -14,6 +14,7 @@ public class Game : BaseEntity public virtual ICollection GameGenres { get; set; } = new HashSet(); public virtual ICollection UserFiles { get; set; } = new HashSet(); public virtual ICollection GameGroups { get; set; } = new HashSet(); + public virtual ICollection GameGoals { get; set; } = new HashSet(); [Required] [StringLength(100)] diff --git a/TASVideos.Data/Entity/Game/GameGoal.cs b/TASVideos.Data/Entity/Game/GameGoal.cs new file mode 100644 index 000000000..1ca3d449b --- /dev/null +++ b/TASVideos.Data/Entity/Game/GameGoal.cs @@ -0,0 +1,15 @@ +namespace TASVideos.Data.Entity.Game; + +public class GameGoal +{ + public int Id { get; set; } + public int GameId { get; set; } + public virtual Game? Game { get; set; } + + [Required] + [StringLength(50)] + public string DisplayName { get; set; } = ""; + + public virtual ICollection Publications { get; set; } = new HashSet(); + public virtual ICollection Submissions { get; set; } = new HashSet(); +} diff --git a/TASVideos.Data/Entity/Publication.cs b/TASVideos.Data/Entity/Publication.cs index 53dec7476..37dddd42e 100644 --- a/TASVideos.Data/Entity/Publication.cs +++ b/TASVideos.Data/Entity/Publication.cs @@ -87,6 +87,9 @@ public class Publication : BaseEntity, ITimeable double ITimeable.FrameRate => SystemFrameRate?.FrameRate ?? throw new InvalidOperationException($"{nameof(SystemFrameRate)} must not be lazy loaded!"); + public int? GameGoalId { get; set; } + public virtual GameGoal? GameGoal { get; set; } + public void GenerateTitle() { var authorList = Authors @@ -115,9 +118,15 @@ public void GenerateTitle() gameName = GameVersion.TitleOverride; } + string goal = GameGoal!.DisplayName; + if (goal == "baseline") + { + goal = ""; + } + Title = $"{System.Code} {gameName}" - + (!string.IsNullOrWhiteSpace(Branch) ? $" \"{Branch}\"" : "") + + (!string.IsNullOrWhiteSpace(goal) ? $" \"{goal}\"" : "") + $" by {string.Join(", ", authorList).LastCommaToAmpersand()}" + $" in {this.Time().ToStringWithOptionalDaysAndHours()}"; } @@ -262,6 +271,7 @@ public static IQueryable IncludeTitleTables(this DbSet .Include(p => p.System) .Include(p => p.SystemFrameRate) .Include(p => p.Game) - .Include(p => p.GameVersion); + .Include(p => p.GameVersion) + .Include(p => p.GameGoal); } } diff --git a/TASVideos.Data/Entity/Submission.cs b/TASVideos.Data/Entity/Submission.cs index ca1ebbd15..083f58634 100644 --- a/TASVideos.Data/Entity/Submission.cs +++ b/TASVideos.Data/Entity/Submission.cs @@ -99,6 +99,9 @@ public class Submission : BaseEntity, ITimeable double ITimeable.FrameRate => SystemFrameRate?.FrameRate ?? 0; + public int? GameGoalId { get; set; } + public virtual GameGoal? GameGoal { get; set; } + public void GenerateTitle() { if (System is null) @@ -132,9 +135,17 @@ public void GenerateTitle() gameName = GameVersion.TitleOverride; } + string? goal = GameGoal?.DisplayName; + goal = goal switch + { + null => Branch, + "baseline" => null, + _ => goal + }; + Title = $"#{Id}: {string.Join(", ", authorList).LastCommaToAmpersand()}'s {System.Code} {gameName}" - + (!string.IsNullOrWhiteSpace(Branch) ? $" \"{Branch}\"" : "") + + (!string.IsNullOrWhiteSpace(goal) ? $" \"{goal}\"" : "") + $" in {this.Time().ToStringWithOptionalDaysAndHours()}"; } @@ -238,6 +249,8 @@ public static IQueryable IncludeTitleTables(this DbSet q .Include(s => s.System) .Include(s => s.SystemFrameRate) .Include(s => s.Game) - .Include(s => s.GameVersion); + .Include(s => s.GameVersion) + .Include(s => s.GameGoal) + .Include(gg => gg.GameGoal); } } diff --git a/TASVideos.Data/Migrations/20240113144410_AddGoal.Designer.cs b/TASVideos.Data/Migrations/20240113144410_AddGoal.Designer.cs new file mode 100644 index 000000000..94c5ba431 --- /dev/null +++ b/TASVideos.Data/Migrations/20240113144410_AddGoal.Designer.cs @@ -0,0 +1,3494 @@ +// +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; +using TASVideos.Data; + +#nullable disable + +namespace TASVideos.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240113144410_AddGoal")] + partial class AddGoal + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pg_trgm"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("TASVideos.Data.CustomAutoHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Changed") + .HasColumnType("citext") + .HasColumnName("changed"); + + b.Property("Created") + .HasColumnType("timestamp without time zone") + .HasColumnName("created"); + + b.Property("Kind") + .HasColumnType("integer") + .HasColumnName("kind"); + + b.Property("RowId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("row_id"); + + b.Property("TableName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("citext") + .HasColumnName("table_name"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_auto_history"); + + b.ToTable("auto_history", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.Award", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("citext") + .HasColumnName("short_name"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_awards"); + + b.ToTable("awards", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.PublicationAward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("integer") + .HasColumnName("award_id"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Year") + .HasColumnType("integer") + .HasColumnName("year"); + + b.HasKey("Id") + .HasName("pk_publication_awards"); + + b.HasIndex("AwardId") + .HasDatabaseName("ix_publication_awards_award_id"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_awards_publication_id"); + + b.ToTable("publication_awards", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.UserAward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("integer") + .HasColumnName("award_id"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("Year") + .HasColumnType("integer") + .HasColumnName("year"); + + b.HasKey("Id") + .HasName("pk_user_awards"); + + b.HasIndex("AwardId") + .HasDatabaseName("ix_user_awards_award_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_awards_user_id"); + + b.ToTable("user_awards", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.DeprecatedMovieFormat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Deprecated") + .HasColumnType("boolean") + .HasColumnName("deprecated"); + + b.Property("FileExtension") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("file_extension"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.HasKey("Id") + .HasName("pk_deprecated_movie_formats"); + + b.HasIndex("FileExtension") + .IsUnique() + .HasDatabaseName("ix_deprecated_movie_formats_file_extension"); + + b.ToTable("deprecated_movie_formats", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Flag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IconPath") + .HasMaxLength(48) + .HasColumnType("citext") + .HasColumnName("icon_path"); + + b.Property("LinkPath") + .HasMaxLength(48) + .HasColumnType("citext") + .HasColumnName("link_path"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("PermissionRestriction") + .HasColumnType("integer") + .HasColumnName("permission_restriction"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(24) + .HasColumnType("citext") + .HasColumnName("token"); + + b.HasKey("Id") + .HasName("pk_flags"); + + b.HasIndex("Token") + .IsUnique() + .HasDatabaseName("ix_flags_token"); + + b.ToTable("flags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.Forum", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("integer") + .HasColumnName("category_id"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.Property("Restricted") + .HasColumnType("boolean") + .HasColumnName("restricted"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("citext") + .HasColumnName("short_name"); + + b.HasKey("Id") + .HasName("pk_forums"); + + b.HasIndex("CategoryId") + .HasDatabaseName("ix_forums_category_id"); + + b.ToTable("forums", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("citext") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("pk_forum_categories"); + + b.ToTable("forum_categories", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPoll", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CloseDate") + .HasColumnType("timestamp without time zone") + .HasColumnName("close_date"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("MultiSelect") + .HasColumnType("boolean") + .HasColumnName("multi_select"); + + b.Property("Question") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("question"); + + b.Property("TopicId") + .HasColumnType("integer") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_forum_polls"); + + b.ToTable("forum_polls", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.Property("PollId") + .HasColumnType("integer") + .HasColumnName("poll_id"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("text"); + + b.HasKey("Id") + .HasName("pk_forum_poll_options"); + + b.HasIndex("PollId") + .HasDatabaseName("ix_forum_poll_options_poll_id"); + + b.ToTable("forum_poll_options", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOptionVote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("ip_address"); + + b.Property("PollOptionId") + .HasColumnType("integer") + .HasColumnName("poll_option_id"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_forum_poll_option_votes"); + + b.HasIndex("PollOptionId") + .HasDatabaseName("ix_forum_poll_option_votes_poll_option_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_forum_poll_option_votes_user_id"); + + b.ToTable("forum_poll_option_votes", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("EnableBbCode") + .HasColumnType("boolean") + .HasColumnName("enable_bb_code"); + + b.Property("EnableHtml") + .HasColumnType("boolean") + .HasColumnName("enable_html"); + + b.Property("ForumId") + .HasColumnType("integer") + .HasColumnName("forum_id"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("ip_address"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("PostEditedTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("post_edited_timestamp"); + + b.Property("PosterId") + .HasColumnType("integer") + .HasColumnName("poster_id"); + + b.Property("PosterMood") + .HasColumnType("integer") + .HasColumnName("poster_mood"); + + b.Property("SearchVector") + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasColumnName("search_vector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Text" }); + + b.Property("Subject") + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("subject"); + + b.Property("Text") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("text"); + + b.Property("TopicId") + .HasColumnType("integer") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_forum_posts"); + + b.HasIndex("ForumId") + .HasDatabaseName("ix_forum_posts_forum_id"); + + b.HasIndex("PosterId") + .HasDatabaseName("ix_forum_posts_poster_id"); + + b.HasIndex("SearchVector") + .HasDatabaseName("ix_forum_posts_search_vector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("SearchVector"), "GIN"); + + b.HasIndex("Text") + .HasDatabaseName("ix_forum_posts_text"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Text"), "gin"); + NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Text"), new[] { "gin_trgm_ops" }); + + b.HasIndex("TopicId") + .HasDatabaseName("ix_forum_posts_topic_id"); + + b.ToTable("forum_posts", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("ForumId") + .HasColumnType("integer") + .HasColumnName("forum_id"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("IsLocked") + .HasColumnType("boolean") + .HasColumnName("is_locked"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("PollId") + .HasColumnType("integer") + .HasColumnName("poll_id"); + + b.Property("PosterId") + .HasColumnType("integer") + .HasColumnName("poster_id"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_forum_topics"); + + b.HasIndex("ForumId") + .HasDatabaseName("ix_forum_topics_forum_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_forum_topics_game_id"); + + b.HasIndex("PollId") + .IsUnique() + .HasDatabaseName("ix_forum_topics_poll_id"); + + b.HasIndex("PosterId") + .HasDatabaseName("ix_forum_topics_poster_id"); + + b.HasIndex("SubmissionId") + .IsUnique() + .HasDatabaseName("ix_forum_topics_submission_id"); + + b.ToTable("forum_topics", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopicWatch", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("ForumTopicId") + .HasColumnType("integer") + .HasColumnName("forum_topic_id"); + + b.Property("IsNotified") + .HasColumnType("boolean") + .HasColumnName("is_notified"); + + b.HasKey("UserId", "ForumTopicId") + .HasName("pk_forum_topic_watches"); + + b.HasIndex("ForumTopicId") + .HasDatabaseName("ix_forum_topic_watches_forum_topic_id"); + + b.ToTable("forum_topic_watches", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Game", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Abbreviation") + .HasMaxLength(24) + .HasColumnType("citext") + .HasColumnName("abbreviation"); + + b.Property("Aliases") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("aliases"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("GameResourcesPage") + .HasMaxLength(300) + .HasColumnType("citext") + .HasColumnName("game_resources_page"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("ScreenshotUrl") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("screenshot_url"); + + b.HasKey("Id") + .HasName("pk_games"); + + b.HasIndex("Abbreviation") + .IsUnique() + .HasDatabaseName("ix_games_abbreviation"); + + b.ToTable("games", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGameGroup", b => + { + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GameGroupId") + .HasColumnType("integer") + .HasColumnName("game_group_id"); + + b.HasKey("GameId", "GameGroupId") + .HasName("pk_game_game_groups"); + + b.HasIndex("GameGroupId") + .HasDatabaseName("ix_game_game_groups_game_group_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_game_groups_game_id"); + + b.ToTable("game_game_groups", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGenre", b => + { + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GenreId") + .HasColumnType("integer") + .HasColumnName("genre_id"); + + b.HasKey("GameId", "GenreId") + .HasName("pk_game_genres"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_genres_game_id"); + + b.HasIndex("GenreId") + .HasDatabaseName("ix_game_genres_genre_id"); + + b.ToTable("game_genres", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.HasKey("Id") + .HasName("pk_game_goals"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_goals_game_id"); + + b.ToTable("game_goals", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Abbreviation") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("abbreviation"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_game_groups"); + + b.HasIndex("Abbreviation") + .IsUnique() + .HasDatabaseName("ix_game_groups_abbreviation"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_game_groups_name"); + + b.ToTable("game_groups", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystem", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id") + .HasAnnotation("DatabaseGenerated", DatabaseGeneratedOption.None); + + b.Property("Code") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("citext") + .HasColumnName("code"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.HasKey("Id") + .HasName("pk_game_systems"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("ix_game_systems_code"); + + b.ToTable("game_systems", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystemFrameRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("FrameRate") + .HasColumnType("double precision") + .HasColumnName("frame_rate"); + + b.Property("GameSystemId") + .HasColumnType("integer") + .HasColumnName("game_system_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Obsolete") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("obsolete"); + + b.Property("Preliminary") + .HasColumnType("boolean") + .HasColumnName("preliminary"); + + b.Property("RegionCode") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("citext") + .HasColumnName("region_code"); + + b.HasKey("Id") + .HasName("pk_game_system_frame_rates"); + + b.HasIndex("GameSystemId") + .HasDatabaseName("ix_game_system_frame_rates_game_system_id"); + + b.ToTable("game_system_frame_rates", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Md5") + .HasMaxLength(32) + .HasColumnType("citext") + .HasColumnName("md5"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Region") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("region"); + + b.Property("Sha1") + .HasMaxLength(40) + .HasColumnType("citext") + .HasColumnName("sha1"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("TitleOverride") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("title_override"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("Version") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("version"); + + b.HasKey("Id") + .HasName("pk_game_versions"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_versions_game_id"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_game_versions_system_id"); + + b.ToTable("game_versions", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.HasKey("Id") + .HasName("pk_genres"); + + b.ToTable("genres", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.IpBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Mask") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("citext") + .HasColumnName("mask"); + + b.HasKey("Id") + .HasName("pk_ip_bans"); + + b.HasIndex("Mask") + .IsUnique() + .HasDatabaseName("ix_ip_bans_mask"); + + b.ToTable("ip_bans", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.MediaPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("citext") + .HasColumnName("body"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("group"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Link") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("link"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("type"); + + b.Property("User") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("user"); + + b.HasKey("Id") + .HasName("pk_media_posts"); + + b.ToTable("media_posts", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PrivateMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DeletedForFromUser") + .HasColumnType("boolean") + .HasColumnName("deleted_for_from_user"); + + b.Property("DeletedForToUser") + .HasColumnType("boolean") + .HasColumnName("deleted_for_to_user"); + + b.Property("EnableBbCode") + .HasColumnType("boolean") + .HasColumnName("enable_bb_code"); + + b.Property("EnableHtml") + .HasColumnType("boolean") + .HasColumnName("enable_html"); + + b.Property("FromUserId") + .HasColumnType("integer") + .HasColumnName("from_user_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("ReadOn") + .HasColumnType("timestamp without time zone") + .HasColumnName("read_on"); + + b.Property("SavedForFromUser") + .HasColumnType("boolean") + .HasColumnName("saved_for_from_user"); + + b.Property("SavedForToUser") + .HasColumnType("boolean") + .HasColumnName("saved_for_to_user"); + + b.Property("Subject") + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("subject"); + + b.Property("Text") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("text"); + + b.Property("ToUserId") + .HasColumnType("integer") + .HasColumnName("to_user_id"); + + b.HasKey("Id") + .HasName("pk_private_messages"); + + b.HasIndex("FromUserId") + .HasDatabaseName("ix_private_messages_from_user_id"); + + b.HasIndex("ToUserId", "ReadOn", "DeletedForToUser") + .HasDatabaseName("ix_private_messages_to_user_id_read_on_deleted_for_to_user"); + + b.ToTable("private_messages", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Publication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdditionalAuthors") + .HasMaxLength(200) + .HasColumnType("citext") + .HasColumnName("additional_authors"); + + b.Property("Branch") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("branch"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("EmulatorVersion") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("emulator_version"); + + b.Property("Frames") + .HasColumnType("integer") + .HasColumnName("frames"); + + b.Property("GameGoalId") + .HasColumnType("integer") + .HasColumnName("game_goal_id"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GameVersionId") + .HasColumnType("integer") + .HasColumnName("game_version_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("MovieFile") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("movie_file"); + + b.Property("MovieFileName") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("movie_file_name"); + + b.Property("ObsoletedById") + .HasColumnType("integer") + .HasColumnName("obsoleted_by_id"); + + b.Property("PublicationClassId") + .HasColumnType("integer") + .HasColumnName("publication_class_id"); + + b.Property("RerecordCount") + .HasColumnType("integer") + .HasColumnName("rerecord_count"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.Property("SystemFrameRateId") + .HasColumnType("integer") + .HasColumnName("system_frame_rate_id"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("Title") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("pk_publications"); + + b.HasIndex("GameGoalId") + .HasDatabaseName("ix_publications_game_goal_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_publications_game_id"); + + b.HasIndex("GameVersionId") + .HasDatabaseName("ix_publications_game_version_id"); + + b.HasIndex("MovieFileName") + .IsUnique() + .HasDatabaseName("ix_publications_movie_file_name"); + + b.HasIndex("ObsoletedById") + .HasDatabaseName("ix_publications_obsoleted_by_id"); + + b.HasIndex("PublicationClassId") + .HasDatabaseName("ix_publications_publication_class_id"); + + b.HasIndex("SubmissionId") + .IsUnique() + .HasDatabaseName("ix_publications_submission_id"); + + b.HasIndex("SystemFrameRateId") + .HasDatabaseName("ix_publications_system_frame_rate_id"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_publications_system_id"); + + b.ToTable("publications", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationAuthor", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.HasKey("UserId", "PublicationId") + .HasName("pk_publication_authors"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_authors_publication_id"); + + b.ToTable("publication_authors", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationClass", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id") + .HasAnnotation("DatabaseGenerated", DatabaseGeneratedOption.None); + + b.Property("IconPath") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("icon_path"); + + b.Property("Link") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("link"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Weight") + .HasColumnType("double precision") + .HasColumnName("weight"); + + b.HasKey("Id") + .HasName("pk_publication_classes"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_publication_classes_name"); + + b.ToTable("publication_classes", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("FileData") + .HasColumnType("bytea") + .HasColumnName("file_data"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("path"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_publication_files"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_files_publication_id"); + + b.ToTable("publication_files", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFlag", b => + { + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("FlagId") + .HasColumnType("integer") + .HasColumnName("flag_id"); + + b.HasKey("PublicationId", "FlagId") + .HasName("pk_publication_flags"); + + b.HasIndex("FlagId") + .HasDatabaseName("ix_publication_flags_flag_id"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_flags_publication_id"); + + b.ToTable("publication_flags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationMaintenanceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Log") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("log"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("TimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("time_stamp"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_publication_maintenance_logs"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_maintenance_logs_publication_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_publication_maintenance_logs_user_id"); + + b.ToTable("publication_maintenance_logs", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationRating", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Value") + .HasColumnType("double precision") + .HasColumnName("value"); + + b.HasKey("UserId", "PublicationId") + .HasName("pk_publication_ratings"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_ratings_publication_id"); + + b.HasIndex("UserId", "PublicationId") + .IsUnique() + .HasDatabaseName("ix_publication_ratings_user_id_publication_id"); + + b.ToTable("publication_ratings", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationTag", b => + { + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("TagId") + .HasColumnType("integer") + .HasColumnName("tag_id"); + + b.HasKey("PublicationId", "TagId") + .HasName("pk_publication_tags"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_tags_publication_id"); + + b.HasIndex("TagId") + .HasDatabaseName("ix_publication_tags_tag_id"); + + b.ToTable("publication_tags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationUrl", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DisplayName") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("url"); + + b.HasKey("Id") + .HasName("pk_publication_urls"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_urls_publication_id"); + + b.HasIndex("Type") + .HasDatabaseName("ix_publication_urls_type"); + + b.ToTable("publication_urls", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoAssignPostCount") + .HasColumnType("integer") + .HasColumnName("auto_assign_post_count"); + + b.Property("AutoAssignPublications") + .HasColumnType("boolean") + .HasColumnName("auto_assign_publications"); + + b.Property("ConcurrencyStamp") + .HasColumnType("citext") + .HasColumnName("concurrency_stamp"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("is_default"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Name") + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("NormalizedName") + .HasColumnType("citext") + .HasColumnName("normalized_name"); + + b.HasKey("Id") + .HasName("pk_roles"); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("citext") + .HasColumnName("claim_type"); + + b.Property("ClaimValue") + .HasColumnType("citext") + .HasColumnName("claim_value"); + + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.HasKey("Id") + .HasName("pk_role_claims"); + + b.HasIndex("RoleId") + .HasDatabaseName("ix_role_claims_role_id"); + + b.ToTable("role_claims", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RoleLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Link") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("citext") + .HasColumnName("link"); + + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.HasKey("Id") + .HasName("pk_role_links"); + + b.HasIndex("RoleId") + .HasDatabaseName("ix_role_links_role_id"); + + b.ToTable("role_links", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.Property("PermissionId") + .HasColumnType("integer") + .HasColumnName("permission_id"); + + b.Property("CanAssign") + .HasColumnType("boolean") + .HasColumnName("can_assign"); + + b.HasKey("RoleId", "PermissionId") + .HasName("pk_role_permission"); + + b.ToTable("role_permission", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Submission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdditionalAuthors") + .HasMaxLength(200) + .HasColumnType("citext") + .HasColumnName("additional_authors"); + + b.Property("Annotations") + .HasColumnType("citext") + .HasColumnName("annotations"); + + b.Property("Branch") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("branch"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("CycleCount") + .HasColumnType("bigint") + .HasColumnName("cycle_count"); + + b.Property("EmulatorVersion") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("emulator_version"); + + b.Property("EncodeEmbedLink") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("encode_embed_link"); + + b.Property("Frames") + .HasColumnType("integer") + .HasColumnName("frames"); + + b.Property("GameGoalId") + .HasColumnType("integer") + .HasColumnName("game_goal_id"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GameName") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("game_name"); + + b.Property("GameVersionId") + .HasColumnType("integer") + .HasColumnName("game_version_id"); + + b.Property("ImportedTime") + .ValueGeneratedOnAdd() + .HasColumnType("numeric(16,4)") + .HasDefaultValue(0m) + .HasColumnName("imported_time"); + + b.Property("IntendedClassId") + .HasColumnType("integer") + .HasColumnName("intended_class_id"); + + b.Property("JudgeId") + .HasColumnType("integer") + .HasColumnName("judge_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("LegacyTime") + .ValueGeneratedOnAdd() + .HasColumnType("numeric(16,4)") + .HasDefaultValue(0m) + .HasColumnName("legacy_time"); + + b.Property("MovieExtension") + .HasColumnType("citext") + .HasColumnName("movie_extension"); + + b.Property("MovieFile") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("movie_file"); + + b.Property("MovieStartType") + .HasColumnType("integer") + .HasColumnName("movie_start_type"); + + b.Property("PublisherId") + .HasColumnType("integer") + .HasColumnName("publisher_id"); + + b.Property("RejectionReasonId") + .HasColumnType("integer") + .HasColumnName("rejection_reason_id"); + + b.Property("RerecordCount") + .HasColumnType("integer") + .HasColumnName("rerecord_count"); + + b.Property("RomName") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("rom_name"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("SubmittedGameVersion") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("submitted_game_version"); + + b.Property("SubmitterId") + .HasColumnType("integer") + .HasColumnName("submitter_id"); + + b.Property("SystemFrameRateId") + .HasColumnType("integer") + .HasColumnName("system_frame_rate_id"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("Title") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("TopicId") + .HasColumnType("integer") + .HasColumnName("topic_id"); + + b.Property("Warnings") + .HasMaxLength(4096) + .HasColumnType("citext") + .HasColumnName("warnings"); + + b.HasKey("Id") + .HasName("pk_submissions"); + + b.HasIndex("GameGoalId") + .HasDatabaseName("ix_submissions_game_goal_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_submissions_game_id"); + + b.HasIndex("GameVersionId") + .HasDatabaseName("ix_submissions_game_version_id"); + + b.HasIndex("IntendedClassId") + .HasDatabaseName("ix_submissions_intended_class_id"); + + b.HasIndex("JudgeId") + .HasDatabaseName("ix_submissions_judge_id"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_submissions_publisher_id"); + + b.HasIndex("RejectionReasonId") + .HasDatabaseName("ix_submissions_rejection_reason_id"); + + b.HasIndex("Status") + .HasDatabaseName("ix_submissions_status"); + + b.HasIndex("SubmitterId") + .HasDatabaseName("ix_submissions_submitter_id"); + + b.HasIndex("SystemFrameRateId") + .HasDatabaseName("ix_submissions_system_frame_rate_id"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_submissions_system_id"); + + b.ToTable("submissions", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionAuthor", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.HasKey("UserId", "SubmissionId") + .HasName("pk_submission_authors"); + + b.HasIndex("SubmissionId") + .HasDatabaseName("ix_submission_authors_submission_id"); + + b.ToTable("submission_authors", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionRejectionReason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.HasKey("Id") + .HasName("pk_submission_rejection_reasons"); + + b.HasIndex("DisplayName") + .IsUnique() + .HasDatabaseName("ix_submission_rejection_reasons_display_name"); + + b.ToTable("submission_rejection_reasons", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.HasKey("Id") + .HasName("pk_submission_status_history"); + + b.HasIndex("SubmissionId") + .HasDatabaseName("ix_submission_status_history_submission_id"); + + b.ToTable("submission_status_history", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("citext") + .HasColumnName("code"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.HasKey("Id") + .HasName("pk_tags"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("ix_tags_code"); + + b.ToTable("tags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("integer") + .HasColumnName("access_failed_count"); + + b.Property("AutoWatchTopic") + .HasColumnType("integer") + .HasColumnName("auto_watch_topic"); + + b.Property("Avatar") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("avatar"); + + b.Property("ConcurrencyStamp") + .HasColumnType("citext") + .HasColumnName("concurrency_stamp"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Email") + .HasColumnType("citext") + .HasColumnName("email"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean") + .HasColumnName("email_confirmed"); + + b.Property("EmailOnPrivateMessage") + .HasColumnType("boolean") + .HasColumnName("email_on_private_message"); + + b.Property("From") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("from"); + + b.Property("LastLoggedInTimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_logged_in_time_stamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean") + .HasColumnName("lockout_enabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone") + .HasColumnName("lockout_end"); + + b.Property("ModeratorComments") + .HasColumnType("citext") + .HasColumnName("moderator_comments"); + + b.Property("MoodAvatarUrlBase") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("mood_avatar_url_base"); + + b.Property("NormalizedEmail") + .HasColumnType("citext") + .HasColumnName("normalized_email"); + + b.Property("NormalizedUserName") + .HasColumnType("citext") + .HasColumnName("normalized_user_name"); + + b.Property("PasswordHash") + .HasColumnType("citext") + .HasColumnName("password_hash"); + + b.Property("PhoneNumber") + .HasColumnType("citext") + .HasColumnName("phone_number"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean") + .HasColumnName("phone_number_confirmed"); + + b.Property("PreferredPronouns") + .HasColumnType("integer") + .HasColumnName("preferred_pronouns"); + + b.Property("PublicRatings") + .HasColumnType("boolean") + .HasColumnName("public_ratings"); + + b.Property("SecurityStamp") + .HasColumnType("citext") + .HasColumnName("security_stamp"); + + b.Property("Signature") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("signature"); + + b.Property("TimeZoneId") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("time_zone_id"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean") + .HasColumnName("two_factor_enabled"); + + b.Property("UseRatings") + .HasColumnType("boolean") + .HasColumnName("use_ratings"); + + b.Property("UserName") + .HasColumnType("citext") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("pk_users"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("ix_users_normalized_user_name"); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("citext") + .HasColumnName("claim_type"); + + b.Property("ClaimValue") + .HasColumnType("citext") + .HasColumnName("claim_value"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_user_claims"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_claims_user_id"); + + b.ToTable("user_claims", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserDisallow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("RegexPattern") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("regex_pattern"); + + b.HasKey("Id") + .HasName("pk_user_disallows"); + + b.HasIndex("RegexPattern") + .IsUnique() + .HasDatabaseName("ix_user_disallows_regex_pattern"); + + b.ToTable("user_disallows", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFile", b => + { + b.Property("Id") + .HasColumnType("bigint") + .HasColumnName("id") + .HasAnnotation("DatabaseGenerated", DatabaseGeneratedOption.None); + + b.Property("Annotations") + .HasColumnType("citext") + .HasColumnName("annotations"); + + b.Property("AuthorId") + .HasColumnType("integer") + .HasColumnName("author_id"); + + b.Property("Class") + .HasColumnType("integer") + .HasColumnName("class"); + + b.Property("CompressionType") + .HasColumnType("integer") + .HasColumnName("compression_type"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("content"); + + b.Property("Description") + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("Downloads") + .HasColumnType("integer") + .HasColumnName("downloads"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("file_name"); + + b.Property("Frames") + .HasColumnType("integer") + .HasColumnName("frames"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("numeric(10,3)") + .HasColumnName("length"); + + b.Property("LogicalLength") + .HasColumnType("integer") + .HasColumnName("logical_length"); + + b.Property("PhysicalLength") + .HasColumnType("integer") + .HasColumnName("physical_length"); + + b.Property("Rerecords") + .HasColumnType("integer") + .HasColumnName("rerecords"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("citext") + .HasColumnName("type"); + + b.Property("UploadTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("upload_timestamp"); + + b.Property("Warnings") + .HasColumnType("citext") + .HasColumnName("warnings"); + + b.HasKey("Id") + .HasName("pk_user_files"); + + b.HasIndex("AuthorId") + .HasDatabaseName("ix_user_files_author_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_user_files_game_id"); + + b.HasIndex("Hidden") + .HasDatabaseName("ix_user_files_hidden"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_user_files_system_id"); + + b.ToTable("user_files", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFileComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("creation_time_stamp"); + + b.Property("Ip") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("ip"); + + b.Property("ParentId") + .HasColumnType("integer") + .HasColumnName("parent_id"); + + b.Property("Text") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("text"); + + b.Property("Title") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("UserFileId") + .HasColumnType("bigint") + .HasColumnName("user_file_id"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_user_file_comments"); + + b.HasIndex("ParentId") + .HasDatabaseName("ix_user_file_comments_parent_id"); + + b.HasIndex("UserFileId") + .HasDatabaseName("ix_user_file_comments_user_file_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_file_comments_user_id"); + + b.ToTable("user_file_comments", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("citext") + .HasColumnName("login_provider"); + + b.Property("ProviderKey") + .HasColumnType("citext") + .HasColumnName("provider_key"); + + b.Property("ProviderDisplayName") + .HasColumnType("citext") + .HasColumnName("provider_display_name"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("LoginProvider", "ProviderKey") + .HasName("pk_user_logins"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_logins_user_id"); + + b.ToTable("user_logins", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserMaintenanceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EditorId") + .HasColumnType("integer") + .HasColumnName("editor_id"); + + b.Property("Log") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("log"); + + b.Property("TimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("time_stamp"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_user_maintenance_logs"); + + b.HasIndex("EditorId") + .HasDatabaseName("ix_user_maintenance_logs_editor_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_maintenance_logs_user_id"); + + b.ToTable("user_maintenance_logs", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserRole", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.HasKey("UserId", "RoleId") + .HasName("pk_user_roles"); + + b.HasIndex("RoleId") + .HasDatabaseName("ix_user_roles_role_id"); + + b.ToTable("user_roles", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserToken", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("LoginProvider") + .HasColumnType("citext") + .HasColumnName("login_provider"); + + b.Property("Name") + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Value") + .HasColumnType("citext") + .HasColumnName("value"); + + b.HasKey("UserId", "LoginProvider", "Name") + .HasName("pk_user_tokens"); + + b.ToTable("user_tokens", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.WikiPage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorId") + .HasColumnType("integer") + .HasColumnName("author_id"); + + b.Property("ChildId") + .HasColumnType("integer") + .HasColumnName("child_id"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Markup") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("markup"); + + b.Property("MinorEdit") + .HasColumnType("boolean") + .HasColumnName("minor_edit"); + + b.Property("PageName") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("page_name"); + + b.Property("Revision") + .HasColumnType("integer") + .HasColumnName("revision"); + + b.Property("RevisionMessage") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("revision_message"); + + b.Property("SearchVector") + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasColumnName("search_vector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "PageName", "Markup" }); + + b.HasKey("Id") + .HasName("pk_wiki_pages"); + + b.HasIndex("AuthorId") + .HasDatabaseName("ix_wiki_pages_author_id"); + + b.HasIndex("ChildId") + .HasDatabaseName("ix_wiki_pages_child_id"); + + b.HasIndex("Markup") + .HasDatabaseName("ix_wiki_pages_markup"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Markup"), "gin"); + NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Markup"), new[] { "gin_trgm_ops" }); + + b.HasIndex("SearchVector") + .HasDatabaseName("ix_wiki_pages_search_vector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("SearchVector"), "GIN"); + + b.HasIndex("PageName", "Revision") + .IsUnique() + .HasDatabaseName("ix_wiki_pages_page_name_revision"); + + b.ToTable("wiki_pages", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.WikiPageReferral", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Excerpt") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("excerpt"); + + b.Property("Referral") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("referral"); + + b.Property("Referrer") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("referrer"); + + b.HasKey("Id") + .HasName("pk_wiki_referrals"); + + b.ToTable("wiki_referrals", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.PublicationAward", b => + { + b.HasOne("TASVideos.Data.Entity.Awards.Award", "Award") + .WithMany() + .HasForeignKey("AwardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_awards_awards_award_id"); + + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationAwards") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_awards_publications_publication_id"); + + b.Navigation("Award"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.UserAward", b => + { + b.HasOne("TASVideos.Data.Entity.Awards.Award", "Award") + .WithMany() + .HasForeignKey("AwardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_awards_awards_award_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserAwards") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_awards_users_user_id"); + + b.Navigation("Award"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.Forum", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumCategory", "Category") + .WithMany("Forums") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forums_forum_categories_category_id"); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOption", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumPoll", "Poll") + .WithMany("PollOptions") + .HasForeignKey("PollId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_poll_options_forum_polls_poll_id"); + + b.Navigation("Poll"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOptionVote", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumPollOption", "PollOption") + .WithMany("Votes") + .HasForeignKey("PollOptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_poll_option_votes_forum_poll_options_poll_option_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_poll_option_votes_users_user_id"); + + b.Navigation("PollOption"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPost", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.Forum", "Forum") + .WithMany("ForumPosts") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_posts_forums_forum_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Poster") + .WithMany("Posts") + .HasForeignKey("PosterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_posts_users_poster_id"); + + b.HasOne("TASVideos.Data.Entity.Forum.ForumTopic", "Topic") + .WithMany("ForumPosts") + .HasForeignKey("TopicId") + .HasConstraintName("fk_forum_posts_forum_topics_topic_id"); + + b.Navigation("Forum"); + + b.Navigation("Poster"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopic", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.Forum", "Forum") + .WithMany("ForumTopics") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_topics_forums_forum_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany() + .HasForeignKey("GameId") + .HasConstraintName("fk_forum_topics_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Forum.ForumPoll", "Poll") + .WithOne("Topic") + .HasForeignKey("TASVideos.Data.Entity.Forum.ForumTopic", "PollId") + .HasConstraintName("fk_forum_topics_forum_polls_poll_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Poster") + .WithMany("Topics") + .HasForeignKey("PosterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_topics_users_poster_id"); + + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithOne("Topic") + .HasForeignKey("TASVideos.Data.Entity.Forum.ForumTopic", "SubmissionId") + .HasConstraintName("fk_forum_topics_submissions_submission_id1"); + + b.Navigation("Forum"); + + b.Navigation("Game"); + + b.Navigation("Poll"); + + b.Navigation("Poster"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopicWatch", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumTopic", "ForumTopic") + .WithMany("ForumTopicWatches") + .HasForeignKey("ForumTopicId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_topic_watches_forum_topics_forum_topic_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("ForumTopicWatches") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_forum_topic_watches_users_user_id"); + + b.Navigation("ForumTopic"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGameGroup", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameGroup", "GameGroup") + .WithMany("Games") + .HasForeignKey("GameGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_game_groups_game_groups_game_group_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameGroups") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_game_groups_games_game_id"); + + b.Navigation("Game"); + + b.Navigation("GameGroup"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGenre", b => + { + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameGenres") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_genres_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Genre", "Genre") + .WithMany("GameGenres") + .HasForeignKey("GenreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_genres_genres_genre_id"); + + b.Navigation("Game"); + + b.Navigation("Genre"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameGoals") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_goals_games_game_id"); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystemFrameRate", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("SystemFrameRates") + .HasForeignKey("GameSystemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_game_system_frame_rates_game_systems_game_system_id"); + + b.Navigation("System"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameVersion", b => + { + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameVersions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_versions_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("GameVersions") + .HasForeignKey("SystemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_versions_game_systems_system_id"); + + b.Navigation("Game"); + + b.Navigation("System"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PrivateMessage", b => + { + b.HasOne("TASVideos.Data.Entity.User", "FromUser") + .WithMany("SentPrivateMessages") + .HasForeignKey("FromUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_private_messages_users_from_user_id"); + + b.HasOne("TASVideos.Data.Entity.User", "ToUser") + .WithMany("ReceivedPrivateMessages") + .HasForeignKey("ToUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_private_messages_users_to_user_id"); + + b.Navigation("FromUser"); + + b.Navigation("ToUser"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Publication", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameGoal", "GameGoal") + .WithMany("Publications") + .HasForeignKey("GameGoalId") + .HasConstraintName("fk_publications_game_goals_game_goal_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("Publications") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameVersion", "GameVersion") + .WithMany("Publications") + .HasForeignKey("GameVersionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_publications_game_versions_game_version_id"); + + b.HasOne("TASVideos.Data.Entity.Publication", "ObsoletedBy") + .WithMany("ObsoletedMovies") + .HasForeignKey("ObsoletedById") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_publications_publications_obsoleted_by_id"); + + b.HasOne("TASVideos.Data.Entity.PublicationClass", "PublicationClass") + .WithMany() + .HasForeignKey("PublicationClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_publication_classes_publication_class_id"); + + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithOne("Publication") + .HasForeignKey("TASVideos.Data.Entity.Publication", "SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_submissions_submission_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystemFrameRate", "SystemFrameRate") + .WithMany() + .HasForeignKey("SystemFrameRateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_game_system_frame_rates_system_frame_rate_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("Publications") + .HasForeignKey("SystemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_publications_game_systems_system_id"); + + b.Navigation("Game"); + + b.Navigation("GameGoal"); + + b.Navigation("GameVersion"); + + b.Navigation("ObsoletedBy"); + + b.Navigation("PublicationClass"); + + b.Navigation("Submission"); + + b.Navigation("System"); + + b.Navigation("SystemFrameRate"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationAuthor", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("Authors") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_authors_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("Publications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_authors_users_user_id"); + + b.Navigation("Author"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFile", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("Files") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_files_publications_publication_id"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFlag", b => + { + b.HasOne("TASVideos.Data.Entity.Flag", "Flag") + .WithMany() + .HasForeignKey("FlagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_flags_flags_flag_id"); + + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationFlags") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_flags_publications_publication_id"); + + b.Navigation("Flag"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationMaintenanceLog", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationMaintenanceLogs") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_maintenance_logs_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("PublicationMaintenanceLogs") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_maintenance_logs_users_user_id"); + + b.Navigation("Publication"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationRating", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationRatings") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_ratings_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("PublicationRatings") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_ratings_users_user_id"); + + b.Navigation("Publication"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationTag", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationTags") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_tags_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.Tag", "Tag") + .WithMany("PublicationTags") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_tags_tags_tag_id"); + + b.Navigation("Publication"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationUrl", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationUrls") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_urls_publications_publication_id"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RoleLink", b => + { + b.HasOne("TASVideos.Data.Entity.Role", "Role") + .WithMany("RoleLinks") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_role_links_roles_role_id"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RolePermission", b => + { + b.HasOne("TASVideos.Data.Entity.Role", "Role") + .WithMany("RolePermission") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_role_permission_roles_role_id"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Submission", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameGoal", "GameGoal") + .WithMany("Submissions") + .HasForeignKey("GameGoalId") + .HasConstraintName("fk_submissions_game_goals_game_goal_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("Submissions") + .HasForeignKey("GameId") + .HasConstraintName("fk_submissions_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameVersion", "GameVersion") + .WithMany("Submissions") + .HasForeignKey("GameVersionId") + .HasConstraintName("fk_submissions_game_versions_game_version_id"); + + b.HasOne("TASVideos.Data.Entity.PublicationClass", "IntendedClass") + .WithMany() + .HasForeignKey("IntendedClassId") + .HasConstraintName("fk_submissions_publication_classes_intended_class_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Judge") + .WithMany() + .HasForeignKey("JudgeId") + .HasConstraintName("fk_submissions_users_judge_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Publisher") + .WithMany() + .HasForeignKey("PublisherId") + .HasConstraintName("fk_submissions_users_publisher_id"); + + b.HasOne("TASVideos.Data.Entity.SubmissionRejectionReason", "RejectionReason") + .WithMany("Submissions") + .HasForeignKey("RejectionReasonId") + .HasConstraintName("fk_submissions_submission_rejection_reasons_rejection_reason_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Submitter") + .WithMany() + .HasForeignKey("SubmitterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submissions_users_submitter_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystemFrameRate", "SystemFrameRate") + .WithMany() + .HasForeignKey("SystemFrameRateId") + .HasConstraintName("fk_submissions_game_system_frame_rates_system_frame_rate_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("Submissions") + .HasForeignKey("SystemId") + .HasConstraintName("fk_submissions_game_systems_system_id"); + + b.Navigation("Game"); + + b.Navigation("GameGoal"); + + b.Navigation("GameVersion"); + + b.Navigation("IntendedClass"); + + b.Navigation("Judge"); + + b.Navigation("Publisher"); + + b.Navigation("RejectionReason"); + + b.Navigation("Submitter"); + + b.Navigation("System"); + + b.Navigation("SystemFrameRate"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionAuthor", b => + { + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithMany("SubmissionAuthors") + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submission_authors_submissions_submission_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("Submissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submission_authors_users_user_id"); + + b.Navigation("Author"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionStatusHistory", b => + { + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithMany("History") + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submission_status_history_submissions_submission_id"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFile", b => + { + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("UserFiles") + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_user_files_users_author_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("UserFiles") + .HasForeignKey("GameId") + .HasConstraintName("fk_user_files_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany() + .HasForeignKey("SystemId") + .HasConstraintName("fk_user_files_game_systems_system_id"); + + b.Navigation("Author"); + + b.Navigation("Game"); + + b.Navigation("System"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFileComment", b => + { + b.HasOne("TASVideos.Data.Entity.UserFileComment", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId") + .HasConstraintName("fk_user_file_comments_user_file_comments_parent_id"); + + b.HasOne("TASVideos.Data.Entity.UserFile", "UserFile") + .WithMany("Comments") + .HasForeignKey("UserFileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_file_comments_user_files_user_file_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserFileComments") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_user_file_comments_users_user_id"); + + b.Navigation("Parent"); + + b.Navigation("User"); + + b.Navigation("UserFile"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserMaintenanceLog", b => + { + b.HasOne("TASVideos.Data.Entity.User", "Editor") + .WithMany("EditMaintenanceLogs") + .HasForeignKey("EditorId") + .HasConstraintName("fk_user_maintenance_logs_users_editor_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserMaintenanceLogs") + .HasForeignKey("UserId") + .HasConstraintName("fk_user_maintenance_logs_users_user_id"); + + b.Navigation("Editor"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserRole", b => + { + b.HasOne("TASVideos.Data.Entity.Role", "Role") + .WithMany("UserRole") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_roles_roles_role_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_roles_users_user_id"); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.WikiPage", b => + { + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("WikiRevisions") + .HasForeignKey("AuthorId") + .HasConstraintName("fk_wiki_pages_users_author_id"); + + b.HasOne("TASVideos.Data.Entity.WikiPage", "Child") + .WithMany() + .HasForeignKey("ChildId") + .HasConstraintName("fk_wiki_pages_wiki_pages_child_id"); + + b.Navigation("Author"); + + b.Navigation("Child"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.Forum", b => + { + b.Navigation("ForumPosts"); + + b.Navigation("ForumTopics"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumCategory", b => + { + b.Navigation("Forums"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPoll", b => + { + b.Navigation("PollOptions"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOption", b => + { + b.Navigation("Votes"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopic", b => + { + b.Navigation("ForumPosts"); + + b.Navigation("ForumTopicWatches"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Game", b => + { + b.Navigation("GameGenres"); + + b.Navigation("GameGoals"); + + b.Navigation("GameGroups"); + + b.Navigation("GameVersions"); + + b.Navigation("Publications"); + + b.Navigation("Submissions"); + + b.Navigation("UserFiles"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.Navigation("Publications"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGroup", b => + { + b.Navigation("Games"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystem", b => + { + b.Navigation("GameVersions"); + + b.Navigation("Publications"); + + b.Navigation("Submissions"); + + b.Navigation("SystemFrameRates"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameVersion", b => + { + b.Navigation("Publications"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Genre", b => + { + b.Navigation("GameGenres"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Publication", b => + { + b.Navigation("Authors"); + + b.Navigation("Files"); + + b.Navigation("ObsoletedMovies"); + + b.Navigation("PublicationAwards"); + + b.Navigation("PublicationFlags"); + + b.Navigation("PublicationMaintenanceLogs"); + + b.Navigation("PublicationRatings"); + + b.Navigation("PublicationTags"); + + b.Navigation("PublicationUrls"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Role", b => + { + b.Navigation("RoleLinks"); + + b.Navigation("RolePermission"); + + b.Navigation("UserRole"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Submission", b => + { + b.Navigation("History"); + + b.Navigation("Publication"); + + b.Navigation("SubmissionAuthors"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionRejectionReason", b => + { + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Tag", b => + { + b.Navigation("PublicationTags"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.User", b => + { + b.Navigation("EditMaintenanceLogs"); + + b.Navigation("ForumTopicWatches"); + + b.Navigation("Posts"); + + b.Navigation("PublicationMaintenanceLogs"); + + b.Navigation("PublicationRatings"); + + b.Navigation("Publications"); + + b.Navigation("ReceivedPrivateMessages"); + + b.Navigation("SentPrivateMessages"); + + b.Navigation("Submissions"); + + b.Navigation("Topics"); + + b.Navigation("UserAwards"); + + b.Navigation("UserFileComments"); + + b.Navigation("UserFiles"); + + b.Navigation("UserMaintenanceLogs"); + + b.Navigation("UserRoles"); + + b.Navigation("WikiRevisions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFile", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFileComment", b => + { + b.Navigation("Children"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TASVideos.Data/Migrations/20240113144410_AddGoal.cs b/TASVideos.Data/Migrations/20240113144410_AddGoal.cs new file mode 100644 index 000000000..69648d7b9 --- /dev/null +++ b/TASVideos.Data/Migrations/20240113144410_AddGoal.cs @@ -0,0 +1,104 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace TASVideos.Data.Migrations +{ + public partial class AddGoal : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "game_goal_id", + table: "submissions", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "game_goal_id", + table: "publications", + type: "integer", + nullable: true); + + migrationBuilder.CreateTable( + name: "game_goals", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + game_id = table.Column(type: "integer", nullable: false), + display_name = table.Column(type: "citext", maxLength: 50, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_game_goals", x => x.id); + table.ForeignKey( + name: "fk_game_goals_games_game_id", + column: x => x.game_id, + principalTable: "games", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_submissions_game_goal_id", + table: "submissions", + column: "game_goal_id"); + + migrationBuilder.CreateIndex( + name: "ix_publications_game_goal_id", + table: "publications", + column: "game_goal_id"); + + migrationBuilder.CreateIndex( + name: "ix_game_goals_game_id", + table: "game_goals", + column: "game_id"); + + migrationBuilder.AddForeignKey( + name: "fk_publications_game_goals_game_goal_id", + table: "publications", + column: "game_goal_id", + principalTable: "game_goals", + principalColumn: "id"); + + migrationBuilder.AddForeignKey( + name: "fk_submissions_game_goals_game_goal_id", + table: "submissions", + column: "game_goal_id", + principalTable: "game_goals", + principalColumn: "id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "fk_publications_game_goals_game_goal_id", + table: "publications"); + + migrationBuilder.DropForeignKey( + name: "fk_submissions_game_goals_game_goal_id", + table: "submissions"); + + migrationBuilder.DropTable( + name: "game_goals"); + + migrationBuilder.DropIndex( + name: "ix_submissions_game_goal_id", + table: "submissions"); + + migrationBuilder.DropIndex( + name: "ix_publications_game_goal_id", + table: "publications"); + + migrationBuilder.DropColumn( + name: "game_goal_id", + table: "submissions"); + + migrationBuilder.DropColumn( + name: "game_goal_id", + table: "publications"); + } + } +} diff --git a/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs index c1d04ccab..beb5d9017 100644 --- a/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -747,6 +747,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("game_genres", (string)null); }); + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.HasKey("Id") + .HasName("pk_game_goals"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_goals_game_id"); + + b.ToTable("game_goals", (string)null); + }); + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGroup", b => { b.Property("Id") @@ -1165,6 +1193,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer") .HasColumnName("frames"); + b.Property("GameGoalId") + .HasColumnType("integer") + .HasColumnName("game_goal_id"); + b.Property("GameId") .HasColumnType("integer") .HasColumnName("game_id"); @@ -1219,6 +1251,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_publications"); + b.HasIndex("GameGoalId") + .HasDatabaseName("ix_publications_game_goal_id"); + b.HasIndex("GameId") .HasDatabaseName("ix_publications_game_id"); @@ -1691,6 +1726,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer") .HasColumnName("frames"); + b.Property("GameGoalId") + .HasColumnType("integer") + .HasColumnName("game_goal_id"); + b.Property("GameId") .HasColumnType("integer") .HasColumnName("game_id"); @@ -1796,6 +1835,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_submissions"); + b.HasIndex("GameGoalId") + .HasDatabaseName("ix_submissions_game_goal_id"); + b.HasIndex("GameId") .HasDatabaseName("ix_submissions_game_id"); @@ -2758,6 +2800,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Genre"); }); + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameGoals") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_goals_games_game_id"); + + b.Navigation("Game"); + }); + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystemFrameRate", b => { b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") @@ -2814,6 +2868,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("TASVideos.Data.Entity.Publication", b => { + b.HasOne("TASVideos.Data.Entity.Game.GameGoal", "GameGoal") + .WithMany("Publications") + .HasForeignKey("GameGoalId") + .HasConstraintName("fk_publications_game_goals_game_goal_id"); + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") .WithMany("Publications") .HasForeignKey("GameId") @@ -2864,6 +2923,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Game"); + b.Navigation("GameGoal"); + b.Navigation("GameVersion"); b.Navigation("ObsoletedBy"); @@ -3032,6 +3093,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("TASVideos.Data.Entity.Submission", b => { + b.HasOne("TASVideos.Data.Entity.Game.GameGoal", "GameGoal") + .WithMany("Submissions") + .HasForeignKey("GameGoalId") + .HasConstraintName("fk_submissions_game_goals_game_goal_id"); + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") .WithMany("Submissions") .HasForeignKey("GameId") @@ -3081,6 +3147,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Game"); + b.Navigation("GameGoal"); + b.Navigation("GameVersion"); b.Navigation("IntendedClass"); @@ -3275,6 +3343,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Navigation("GameGenres"); + b.Navigation("GameGoals"); + b.Navigation("GameGroups"); b.Navigation("GameVersions"); @@ -3286,6 +3356,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("UserFiles"); }); + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.Navigation("Publications"); + + b.Navigation("Submissions"); + }); + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGroup", b => { b.Navigation("Games"); diff --git a/TASVideos/Extensions/EntityExtensions.cs b/TASVideos/Extensions/EntityExtensions.cs index fa39f1457..7dc3c9dd9 100644 --- a/TASVideos/Extensions/EntityExtensions.cs +++ b/TASVideos/Extensions/EntityExtensions.cs @@ -367,7 +367,7 @@ public static IQueryable ToMiniMovieModel(this IQueryable f.Type == FileType.Screenshot) .Select(f => new MiniMovieModel.ScreenshotFile @@ -400,7 +400,8 @@ public static IQueryable ToPublishModel(this IQueryable< SystemFrameRateId = s.SystemFrameRateId, Status = s.Status, EmulatorVersion = s.EmulatorVersion, - Branch = s.Branch + Branch = s.Branch, + GameGoalId = s.GameGoalId }); } diff --git a/TASVideos/Pages/Games/Goals/List.cshtml b/TASVideos/Pages/Games/Goals/List.cshtml new file mode 100644 index 000000000..c5cce5ed3 --- /dev/null +++ b/TASVideos/Pages/Games/Goals/List.cshtml @@ -0,0 +1,70 @@ +@page "/Games/{gameId}/Goals/List" +@model ListModel +@{ + ViewData.SetTitle($"Goals for {Model.GameDisplayName}"); + string returnUrl = HttpContext.Request.ReturnUrl(); +} + +

Game: @Model.GameDisplayName + Back +

+ + + + + + + + @foreach (var goal in Model.Goals + .OrderByDescending(g => g.DisplayName == "baseline") + .ThenByDescending(g => g.Publications.Any(p => !p.Obs)) + .ThenBy(g => g.DisplayName.Length) + .ThenBy(g => g.DisplayName)) + { + var canDelete = !goal.Publications.Any() && !goal.Submissions.Any(); + + + + + + + + } + + + + + + +
@Html.DisplayNameFor(m => m.Goals.First().DisplayName)PublicationsSubmissionsActions
+
+ + + +
+
+ @goal.DisplayName + @foreach (var pub in goal.Publications.OrderBy(p => p.Obs).ThenByDescending(p => p.Id)) + { + (Obsolete) + + @pub.Title +
+ } +
+ @foreach (var sub in goal.Submissions.OrderByDescending(s => s.Id)) + { + @sub.Title
+ } +
+
+ Edit + +
+
+ + + +
diff --git a/TASVideos/Pages/Games/Goals/List.cshtml.cs b/TASVideos/Pages/Games/Goals/List.cshtml.cs new file mode 100644 index 000000000..eeb538434 --- /dev/null +++ b/TASVideos/Pages/Games/Goals/List.cshtml.cs @@ -0,0 +1,174 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; +using TASVideos.Data; +using TASVideos.Data.Entity; +using TASVideos.Data.Entity.Game; +using TASVideos.Pages.Games.Goals.Models; + +namespace TASVideos.Pages.Games.Goals; + +public class ListModel : BasePageModel +{ + private readonly ApplicationDbContext _db; + + public ListModel(ApplicationDbContext db) + { + _db = db; + } + + [Display(Name = "Game")] + public string GameDisplayName { get; set; } = ""; + + [FromRoute] + public int GameId { get; set; } + + [FromQuery] + public int? GoalToEdit { get; set; } + + public List Goals { get; set; } = new(); + + public async Task OnGet() + { + var gameDisplayName = await _db.Games + .Where(g => g.Id == GameId). + Select(g => g.DisplayName) + .SingleOrDefaultAsync(); + + if (gameDisplayName is null) + { + return NotFound(); + } + + GameDisplayName = gameDisplayName; + + Goals = await _db.GameGoals + .Where(gg => gg.GameId == GameId) + .Select(gg => new GoalListModel + { + Id = gg.Id, + DisplayName = gg.DisplayName, + Publications = gg.Publications + .Select(p => new GoalListModel.PublicationEntry(p.Id, p.Title, p.ObsoletedById.HasValue)), + Submissions = gg.Submissions + .Select(s => new GoalListModel.SubmissionEntry(s.Id, s.Title)) + }) + .ToListAsync(); + + return Page(); + } + + public async Task OnPost(string? goalToCreate) + { + if (string.IsNullOrWhiteSpace(goalToCreate)) + { + ErrorStatusMessage("Cannot create empty goal"); + return BackToList(); + } + + if (await _db.GameGoals.AnyAsync(gg => gg.GameId == GameId && gg.DisplayName == goalToCreate)) + { + ErrorStatusMessage($"Cannot create goal {goalToCreate} because it already exists."); + return BackToList(); + } + + _db.GameGoals.Add(new GameGoal + { + GameId = GameId, + DisplayName = goalToCreate + }); + + await ConcurrentSave(_db, $"Goal {goalToCreate} created successfully", $"Unable to create goal {goalToCreate}"); + return BackToList(); + } + + public async Task OnPostEdit(int gameGoalId, string? newGoalName) + { + if (!User.Has(PermissionTo.CatalogMovies)) + { + return AccessDenied(); + } + + if (string.IsNullOrWhiteSpace(newGoalName)) + { + ErrorStatusMessage("Cannot create empty goal"); + return BackToList(); + } + + var gameGoal = await _db.GameGoals + .SingleOrDefaultAsync(gg => gg.Id == gameGoalId); + if (gameGoal is null) + { + return NotFound(); + } + + var oldGoalName = gameGoal.DisplayName; + + if (gameGoal.DisplayName.ToLower() == newGoalName.ToLower()) + { + gameGoal.DisplayName = newGoalName; + await ConcurrentSave(_db, $"Goal changed from {oldGoalName} to {newGoalName} successfully", $"Unable to change goal from {oldGoalName} to {newGoalName}"); + return BackToList(); + } + + if (gameGoal.DisplayName == "baseline") + { + ErrorStatusMessage("Cannot edit baseline goal."); + return BackToList(); + } + + if (await _db.GameGoals.AnyAsync(gg => gg.GameId == GameId && gg.DisplayName == newGoalName)) + { + ErrorStatusMessage($"Cannot change goal to {newGoalName} because it already exists."); + return BackToList(); + } + + gameGoal.DisplayName = newGoalName; + + await ConcurrentSave(_db, $"Goal changed from {oldGoalName} to {newGoalName} successfully", $"Unable to change goal from {oldGoalName} to {newGoalName}"); + + // Update publication and submission titles + if (gameGoal.DisplayName != "baseline") + { + var pubs = await _db.Publications.IncludeTitleTables().Where(p => p.GameGoalId == gameGoal.Id).ToListAsync(); + foreach (var pub in pubs) + { + pub.GenerateTitle(); + } + + var subs = await _db.Submissions.IncludeTitleTables().Where(s => s.GameGoalId == gameGoal.Id).ToListAsync(); + foreach (var sub in subs) + { + sub.GenerateTitle(); + } + + await _db.SaveChangesAsync(); + } + + return BackToList(); + } + + public async Task OnGetDelete(int gameGroupId) + { + if (await _db.Publications.AnyAsync(p => p.GameGoalId == gameGroupId)) + { + ErrorStatusMessage("Game Group can not be deleted because it is associated with one or more publications."); + return BackToList(); + } + + if (await _db.Publications.AnyAsync(p => p.GameGoalId == gameGroupId)) + { + ErrorStatusMessage("Game Group can not be deleted because it is associated with one or more submissions."); + return BackToList(); + } + + _db.GameGoals.Attach(new GameGoal { Id = gameGroupId }).State = EntityState.Deleted; + await ConcurrentSave(_db, $"Game Group {gameGroupId} deleted", $"Unable to delete Game Group {gameGroupId}"); + + return BackToList(); + } + + private IActionResult BackToList() + { + return BasePageRedirect("List", new { GameId }); + } +} diff --git a/TASVideos/Pages/Games/Goals/Models/GoalListModel.cs b/TASVideos/Pages/Games/Goals/Models/GoalListModel.cs new file mode 100644 index 000000000..c5efe1494 --- /dev/null +++ b/TASVideos/Pages/Games/Goals/Models/GoalListModel.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace TASVideos.Pages.Games.Goals.Models; + +public class GoalListModel +{ + public int Id { get; set; } + + [Display(Name = "Name")] + public string DisplayName { get; set; } = ""; + + public IEnumerable Publications { get; set; } = new List(); + + public IEnumerable Submissions { get; set; } = new List(); + + public record PublicationEntry(int Id, string Title, bool Obs); + + public record SubmissionEntry(int Id, string Title); +} diff --git a/TASVideos/Pages/Games/Index.cshtml b/TASVideos/Pages/Games/Index.cshtml index 0e98b6328..a18735cf0 100644 --- a/TASVideos/Pages/Games/Index.cshtml +++ b/TASVideos/Pages/Games/Index.cshtml @@ -109,7 +109,7 @@ } - The baseline tab shows the default movie beating the game as fast as possible without any special conditions. + The baseline tab shows the default movie beating the game as fast as possible without any special conditions.
diff --git a/TASVideos/Pages/Games/Index.cshtml.cs b/TASVideos/Pages/Games/Index.cshtml.cs index 308e1ae9b..60b1ed616 100644 --- a/TASVideos/Pages/Games/Index.cshtml.cs +++ b/TASVideos/Pages/Games/Index.cshtml.cs @@ -52,13 +52,13 @@ public async Task OnGet() Game = game; var movies = await _db.Publications .Where(p => p.GameId == Game.Id && p.ObsoletedById == null) - .OrderBy(p => p.Branch == null ? -1 : p.Branch.Length) + .OrderBy(p => p.GameGoal!.DisplayName.Length) .ThenBy(p => p.Frames) .Select(p => new { p.Id, p.Title, - Branch = p.Branch ?? "", + Goal = p.GameGoal!.DisplayName, Screenshot = p.Files .Where(f => f.Type == FileType.Screenshot) .Select(f => new MiniMovieModel.ScreenshotFile @@ -75,13 +75,13 @@ public async Task OnGet() Movies = movies .Select(m => new TabMiniMovieModel( - movies.Count(mm => mm.Branch == m.Branch) > 1 ? m.GameTitle : string.IsNullOrEmpty(m.Branch) ? "(baseline)" : "", - m.Branch, + movies.Count(mm => mm.Goal == m.Goal) > 1 ? m.GameTitle : "", + m.Goal, new MiniMovieModel { Id = m.Id, Title = m.Title, - Branch = m.Branch, + Goal = m.Goal, Screenshot = m.Screenshot, OnlineWatchingUrl = m.OnlineWatchingUrl, })) diff --git a/TASVideos/Pages/Games/List.cshtml.cs b/TASVideos/Pages/Games/List.cshtml.cs index 962c89101..381f03e65 100644 --- a/TASVideos/Pages/Games/List.cshtml.cs +++ b/TASVideos/Pages/Games/List.cshtml.cs @@ -144,6 +144,30 @@ public async Task OnGetVersionDropDownForGame(int gameId, int sys }; } + public async Task OnGetGameGoalDropDownForGame(int gameId, bool includeEmpty) + { + var items = await _db.GameGoals + .Where(gg => gg.GameId == gameId) + .OrderBy(gg => gg.DisplayName) + .Select(gg => new SelectListItem + { + Value = gg.Id.ToString(), + Text = gg.DisplayName + }) + .ToListAsync(); + + if (includeEmpty) + { + items = UiDefaults.DefaultEntry.Concat(items).ToList(); + } + + return new PartialViewResult + { + ViewName = "_DropdownItems", + ViewData = new ViewDataDictionary>(ViewData, items) + }; + } + private async Task> GetPageOfGames(GameListRequest paging) { PageOf data; diff --git a/TASVideos/Pages/Publications/Catalog.cshtml b/TASVideos/Pages/Publications/Catalog.cshtml index c3a90ccf8..93f422381 100644 --- a/TASVideos/Pages/Publications/Catalog.cshtml +++ b/TASVideos/Pages/Publications/Catalog.cshtml @@ -47,6 +47,20 @@ +
+ + + + +
+ + +
+
+ +
+
+

@@ -69,6 +83,7 @@ "@Html.IdFor(m => m.Catalog.SystemFrameRateId)", "@Html.IdFor(m => m.Catalog.GameId)", "@Html.IdFor(m => m.Catalog.GameVersionId)", + "@Html.IdFor(m => m.Catalog.GameGoalId)", "@HttpContext.CurrentPathToReturnUrl()"); } diff --git a/TASVideos/Pages/Publications/Catalog.cshtml.cs b/TASVideos/Pages/Publications/Catalog.cshtml.cs index 9392834ee..f92126c0d 100644 --- a/TASVideos/Pages/Publications/Catalog.cshtml.cs +++ b/TASVideos/Pages/Publications/Catalog.cshtml.cs @@ -39,6 +39,7 @@ public CatalogModel( public IEnumerable AvailableGames { get; set; } = new List(); public IEnumerable AvailableSystems { get; set; } = new List(); public IEnumerable AvailableSystemFrameRates { get; set; } = new List(); + public IEnumerable AvailableGoals { get; set; } = new List(); public async Task OnGet() { @@ -50,7 +51,8 @@ public async Task OnGet() GameVersionId = p.GameVersionId, GameId = p.GameId, SystemId = p.SystemId, - SystemFrameRateId = p.SystemFrameRateId + SystemFrameRateId = p.SystemFrameRateId, + GameGoalId = p.GameGoalId!.Value }) .SingleOrDefaultAsync(); @@ -147,6 +149,21 @@ public async Task OnPost() } } + if (publication.GameGoalId != Catalog.GameGoalId) + { + var gameGoal = await _db.GameGoals.SingleOrDefaultAsync(gg => gg.Id == Catalog.GameGoalId); + if (gameGoal is null) + { + ModelState.AddModelError($"{nameof(Catalog)}.{nameof(Catalog.GameGoalId)}", $"Unknown Game Goal Id: {Catalog.GameGoalId}"); + } + else + { + externalMessages.Add($"Game Goal changed from {publication.GameGoal!.DisplayName} to {gameGoal.DisplayName}"); + publication.GameGoalId = Catalog.GameGoalId; + publication.GameGoal = gameGoal; + } + } + if (publication.GameVersionId != Catalog.GameVersionId) { var gameVersion = await _db.GameVersions.SingleOrDefaultAsync(s => s.Id == Catalog.GameVersionId); @@ -219,5 +236,14 @@ private async Task PopulateCatalogDropDowns(int gameId, int systemId) .ForSystem(systemId) .ToDropDown() .ToListAsync(); + + AvailableGoals = await _db.GameGoals + .Where(gg => gg.GameId == gameId) + .Select(gg => new SelectListItem + { + Value = gg.Id.ToString(), + Text = gg.DisplayName + }) + .ToListAsync(); } } diff --git a/TASVideos/Pages/Publications/Edit.cshtml b/TASVideos/Pages/Publications/Edit.cshtml index 86af38f61..ea1a0be65 100644 --- a/TASVideos/Pages/Publications/Edit.cshtml +++ b/TASVideos/Pages/Publications/Edit.cshtml @@ -65,18 +65,11 @@
-
- - - -
-
-
@@ -84,7 +77,7 @@
+
+
diff --git a/TASVideos/Pages/Publications/Edit.cshtml.cs b/TASVideos/Pages/Publications/Edit.cshtml.cs index 663cb7452..d12c5dfd7 100644 --- a/TASVideos/Pages/Publications/Edit.cshtml.cs +++ b/TASVideos/Pages/Publications/Edit.cshtml.cs @@ -68,7 +68,6 @@ public async Task OnGet() Title = p.Title, ObsoletedBy = p.ObsoletedById, ObsoletedByTitle = p.ObsoletedBy != null ? p.ObsoletedBy.Title : null, - Branch = p.Branch, EmulatorVersion = p.EmulatorVersion, AdditionalAuthors = p.AdditionalAuthors, Urls = p.PublicationUrls @@ -177,13 +176,6 @@ private async Task UpdatePublication(int id, PublicationEditModel model) .ToList(); Publication.Authors = pubAuthors; - if (publication.Branch != model.Branch) - { - externalMessages.Add($"Changed branch from \"{publication.Branch}\" to \"{model.Branch}\""); - } - - publication.Branch = model.Branch; - if (publication.ObsoletedById != model.ObsoletedBy) { externalMessages.Add($"Changed obsoleting movie from \"{publication.ObsoletedById}\" to \"{model.ObsoletedBy}\""); diff --git a/TASVideos/Pages/Publications/Models/PublicationCatalogModel.cs b/TASVideos/Pages/Publications/Models/PublicationCatalogModel.cs index df1a8a24a..b418bb4c1 100644 --- a/TASVideos/Pages/Publications/Models/PublicationCatalogModel.cs +++ b/TASVideos/Pages/Publications/Models/PublicationCatalogModel.cs @@ -10,6 +10,10 @@ public class PublicationCatalogModel [Display(Name = "Game Version")] public int GameVersionId { get; set; } + [Required] + [Display(Name = "Goal")] + public int GameGoalId { get; set; } + [Required] [Display(Name = "Game")] public int GameId { get; set; } diff --git a/TASVideos/Pages/Publications/Models/PublicationEditModel.cs b/TASVideos/Pages/Publications/Models/PublicationEditModel.cs index f88e4ca01..f5df15193 100644 --- a/TASVideos/Pages/Publications/Models/PublicationEditModel.cs +++ b/TASVideos/Pages/Publications/Models/PublicationEditModel.cs @@ -32,8 +32,6 @@ public class PublicationEditModel [Display(Name = "Emulator Version")] public string? EmulatorVersion { get; set; } - public string? Branch { get; set; } - [Display(Name = "Selected Flags")] public IEnumerable SelectedFlags { get; set; } = new List(); diff --git a/TASVideos/Pages/Shared/Components/TabularMovieList/Default.cshtml b/TASVideos/Pages/Shared/Components/TabularMovieList/Default.cshtml index 21f061f80..3fc39b09c 100644 --- a/TASVideos/Pages/Shared/Components/TabularMovieList/Default.cshtml +++ b/TASVideos/Pages/Shared/Components/TabularMovieList/Default.cshtml @@ -9,6 +9,7 @@
@foreach (var pub in Model) { + var goal = pub.Goal == "baseline" ? "" : pub.Goal;
@@ -28,7 +29,7 @@
- @pub.Game "@pub.Branch" + @pub.Game "@goal"
diff --git a/TASVideos/Pages/Shared/_PublicationHistory.cshtml b/TASVideos/Pages/Shared/_PublicationHistory.cshtml index ea46c2083..f351c7e54 100644 --- a/TASVideos/Pages/Shared/_PublicationHistory.cshtml +++ b/TASVideos/Pages/Shared/_PublicationHistory.cshtml @@ -1,10 +1,10 @@ @model PublicationHistoryGroup -@foreach (var node in Model.Branches.OrderBy(b => string.IsNullOrWhiteSpace(b.Branch))) +@foreach (var node in Model.Goals.OrderBy(b => string.IsNullOrWhiteSpace(b.Goal))) { var highlight = node.Id == (int?)ViewData["Highlight"] ? ViewData["HighlightClass"] : "";
-

Current: @node.Branch

+

Current: @node.Goal

@foreach (var flag in node.Flags.Where(f => !string.IsNullOrWhiteSpace(f.IconPath))) { diff --git a/TASVideos/Pages/Submissions/Catalog.cshtml b/TASVideos/Pages/Submissions/Catalog.cshtml index e8c4bf0ec..48c4975e1 100644 --- a/TASVideos/Pages/Submissions/Catalog.cshtml +++ b/TASVideos/Pages/Submissions/Catalog.cshtml @@ -48,6 +48,20 @@
+
+ + + + +
+ + +
+
+ +
+
+

@@ -70,6 +84,7 @@ "@Html.IdFor(m => m.Catalog.SystemFrameRateId)", "@Html.IdFor(m => m.Catalog.GameId)", "@Html.IdFor(m => m.Catalog.GameVersionId)", + "@Html.IdFor(m => m.Catalog.GameGoalId)", "@HttpContext.CurrentPathToReturnUrl()"); } diff --git a/TASVideos/Pages/Submissions/Catalog.cshtml.cs b/TASVideos/Pages/Submissions/Catalog.cshtml.cs index 7c76ea3fd..7290bd804 100644 --- a/TASVideos/Pages/Submissions/Catalog.cshtml.cs +++ b/TASVideos/Pages/Submissions/Catalog.cshtml.cs @@ -39,6 +39,7 @@ public CatalogModel( public IEnumerable AvailableGames { get; set; } = new List(); public IEnumerable AvailableSystems { get; set; } = new List(); public IEnumerable AvailableSystemFrameRates { get; set; } = new List(); + public IEnumerable AvailableGoals { get; set; } = new List(); public async Task OnGet() { @@ -50,7 +51,8 @@ public async Task OnGet() GameVersionId = s.GameVersionId, GameId = s.GameId, SystemId = s.SystemId, - SystemFrameRateId = s.SystemFrameRateId + SystemFrameRateId = s.SystemFrameRateId, + GameGoalId = s.GameGoalId }) .SingleOrDefaultAsync(); @@ -155,6 +157,30 @@ public async Task OnPost() } } + if (submission.GameGoalId != Catalog.GameGoalId) + { + if (Catalog.GameGoalId.HasValue) + { + var gameGoal = await _db.GameGoals.SingleOrDefaultAsync(gg => gg.Id == Catalog.GameGoalId); + if (gameGoal is null) + { + ModelState.AddModelError($"{nameof(Catalog)}.{nameof(Catalog.GameGoalId)}", $"Unknown Game Goal Id: {Catalog.GameGoalId}"); + } + else + { + externalMessages.Add($"Game Goal changed from {submission.GameGoal?.DisplayName ?? "\"\""} to {gameGoal.DisplayName}"); + submission.GameGoalId = Catalog.GameGoalId; + submission.GameGoal = gameGoal; + } + } + else + { + externalMessages.Add("Game Goal removed"); + submission.GameGoalId = null; + submission.GameGoal = null; + } + } + if (submission.GameVersionId != Catalog.GameVersionId) { if (Catalog.GameVersionId.HasValue) @@ -268,5 +294,16 @@ private async Task PopulateCatalogDropDowns() .ToDropDown() .ToListAsync() : new List(); + + AvailableGoals = Catalog.GameId.HasValue + ? await _db.GameGoals + .Where(gg => gg.GameId == Catalog.GameId) + .Select(gg => new SelectListItem + { + Value = gg.Id.ToString(), + Text = gg.DisplayName + }) + .ToListAsync() + : new List(); } } diff --git a/TASVideos/Pages/Submissions/Models/SubmissionCatalogModel.cs b/TASVideos/Pages/Submissions/Models/SubmissionCatalogModel.cs index 5cb77f0a7..bdcc87840 100644 --- a/TASVideos/Pages/Submissions/Models/SubmissionCatalogModel.cs +++ b/TASVideos/Pages/Submissions/Models/SubmissionCatalogModel.cs @@ -20,5 +20,8 @@ public class SubmissionCatalogModel [Required] public int? SystemFrameRateId { get; set; } + [Display(Name = "Goal")] + public int? GameGoalId { get; set; } + public bool MinorEdit { get; set; } } diff --git a/TASVideos/Pages/Submissions/Models/SubmissionCreateModel.cs b/TASVideos/Pages/Submissions/Models/SubmissionCreateModel.cs index 4e57bb159..143e815e5 100644 --- a/TASVideos/Pages/Submissions/Models/SubmissionCreateModel.cs +++ b/TASVideos/Pages/Submissions/Models/SubmissionCreateModel.cs @@ -15,7 +15,7 @@ public class SubmissionCreateModel [StringLength(100)] public string GameName { get; set; } = ""; - [Display(Name = "Branch Name", Description = "Example: 100% or princess only; any% can usually be omitted")] + [Display(Name = "Goal Name", Description = "Example: 100% or princess only; any% can usually be omitted")] [StringLength(50)] public string? Branch { get; set; } diff --git a/TASVideos/Pages/Submissions/Models/SubmissionDisplayModel.cs b/TASVideos/Pages/Submissions/Models/SubmissionDisplayModel.cs index 98213f317..54a94a959 100644 --- a/TASVideos/Pages/Submissions/Models/SubmissionDisplayModel.cs +++ b/TASVideos/Pages/Submissions/Models/SubmissionDisplayModel.cs @@ -35,9 +35,11 @@ public class SubmissionDisplayModel : ISubmissionDisplay [Display(Name = "ROM Filename")] public string? RomName { get; set; } - [Display(Name = "Branch")] + [Display(Name = "Goal")] public string? Branch { get; set; } + public string? Goal { get; set; } + [Display(Name = "Emulator")] public string? Emulator { get; set; } diff --git a/TASVideos/Pages/Submissions/Models/SubmissionEditModel.cs b/TASVideos/Pages/Submissions/Models/SubmissionEditModel.cs index 43dd9815c..0807f1b3c 100644 --- a/TASVideos/Pages/Submissions/Models/SubmissionEditModel.cs +++ b/TASVideos/Pages/Submissions/Models/SubmissionEditModel.cs @@ -47,7 +47,7 @@ public class SubmissionEditModel [Display(Name = "ROM filename")] public string? RomName { get; set; } - [Display(Name = "Branch")] + [Display(Name = "Goal")] public string? Branch { get; set; } [Display(Name = "Emulator", Description = "Needs to be a specific version that sync was verified on. Does not necessarily need to be the version used by the author.")] diff --git a/TASVideos/Pages/Submissions/Models/SubmissionPublishModel.cs b/TASVideos/Pages/Submissions/Models/SubmissionPublishModel.cs index f707c42a4..3b6e5180f 100644 --- a/TASVideos/Pages/Submissions/Models/SubmissionPublishModel.cs +++ b/TASVideos/Pages/Submissions/Models/SubmissionPublishModel.cs @@ -75,6 +75,7 @@ public class SubmissionPublishModel public int SystemId { get; set; } public int? SystemFrameRateId { get; set; } public SubmissionStatus Status { get; set; } + public int? GameGoalId { get; set; } [Display(Name = "Emulator Version")] public string? EmulatorVersion { get; set; } @@ -84,6 +85,7 @@ public class SubmissionPublishModel && SystemFrameRateId.HasValue && GameId > 0 && VersionId > 0 + && GameGoalId > 0 && !string.IsNullOrEmpty(PublicationClass) && Status == SubmissionStatus.PublicationUnderway; } diff --git a/TASVideos/Pages/Submissions/Publish.cshtml.cs b/TASVideos/Pages/Submissions/Publish.cshtml.cs index affdd0ad0..2c5646ff3 100644 --- a/TASVideos/Pages/Submissions/Publish.cshtml.cs +++ b/TASVideos/Pages/Submissions/Publish.cshtml.cs @@ -122,14 +122,14 @@ public async Task OnPost() SystemFrameRateId = submission.SystemFrameRate!.Id, GameId = submission.Game!.Id, GameVersionId = submission.GameVersion!.Id, - Branch = submission.Branch, EmulatorVersion = submission.EmulatorVersion, Frames = submission.Frames, RerecordCount = submission.RerecordCount, MovieFileName = movieFileName, AdditionalAuthors = submission.AdditionalAuthors, Submission = submission, - MovieFile = await _fileService.CopyZip(submission.MovieFile, movieFileName) + MovieFile = await _fileService.CopyZip(submission.MovieFile, movieFileName), + GameGoalId = submission.GameGoalId }; publication.PublicationUrls.AddStreaming(Submission.OnlineWatchingUrl, Submission.OnlineWatchUrlName); diff --git a/TASVideos/Pages/Submissions/View.cshtml b/TASVideos/Pages/Submissions/View.cshtml index 962604b93..e5ad78c3e 100644 --- a/TASVideos/Pages/Submissions/View.cshtml +++ b/TASVideos/Pages/Submissions/View.cshtml @@ -108,10 +108,14 @@
@Model.Submission.SubmittedGameName
-
- +
+
@Model.Submission.Branch
+
+ +
@Model.Submission.Goal
+
diff --git a/TASVideos/Pages/Submissions/View.cshtml.cs b/TASVideos/Pages/Submissions/View.cshtml.cs index 8c00f1c89..0067429d2 100644 --- a/TASVideos/Pages/Submissions/View.cshtml.cs +++ b/TASVideos/Pages/Submissions/View.cshtml.cs @@ -46,6 +46,9 @@ public async Task OnGet() GameVersion = s.GameVersionId != null ? s.GameVersion!.Name : s.SubmittedGameVersion, RomName = s.RomName, Branch = s.Branch, + Goal = s.GameGoal != null + ? s.GameGoal!.DisplayName + : null, Emulator = s.EmulatorVersion, FrameCount = s.Frames, FrameRate = s.SystemFrameRate!.FrameRate, diff --git a/TASVideos/ViewComponents/Models/MiniMovieModel.cs b/TASVideos/ViewComponents/Models/MiniMovieModel.cs index f8f2788a7..b6b67afa7 100644 --- a/TASVideos/ViewComponents/Models/MiniMovieModel.cs +++ b/TASVideos/ViewComponents/Models/MiniMovieModel.cs @@ -4,7 +4,7 @@ public class MiniMovieModel { public int Id { get; init; } public string Title { get; init; } = ""; - public string Branch { get; init; } = ""; + public string Goal { get; init; } = ""; public ScreenshotFile Screenshot { get; init; } = new(); public string? OnlineWatchingUrl { get; init; } diff --git a/TASVideos/ViewComponents/Models/TabularMovieListModel.cs b/TASVideos/ViewComponents/Models/TabularMovieListModel.cs index 5cbae76b2..ac855e3d8 100644 --- a/TASVideos/ViewComponents/Models/TabularMovieListModel.cs +++ b/TASVideos/ViewComponents/Models/TabularMovieListModel.cs @@ -18,7 +18,7 @@ public class TabularMovieListResultModel : ITimeable public string System { get; set; } = ""; public string Game { get; set; } = ""; - public string? Branch { get; set; } + public string? Goal { get; set; } public IEnumerable? Authors { get; set; } public string? AdditionalAuthors { get; set; } diff --git a/TASVideos/ViewComponents/TabularMovieList.cs b/TASVideos/ViewComponents/TabularMovieList.cs index 1e0862592..a1de5a909 100644 --- a/TASVideos/ViewComponents/TabularMovieList.cs +++ b/TASVideos/ViewComponents/TabularMovieList.cs @@ -56,7 +56,7 @@ private async Task> MovieList(TabularMo FrameRate = p.SystemFrameRate!.FrameRate, System = p.System!.Code, Game = p.GameVersion != null && !string.IsNullOrEmpty(p.GameVersion.TitleOverride) ? p.GameVersion.TitleOverride : p.Game!.DisplayName, - Branch = p.Branch, + Goal = p.GameGoal!.DisplayName, Authors = p.Authors.OrderBy(pa => pa.Ordinal).Select(pa => pa.Author!.UserName), AdditionalAuthors = p.AdditionalAuthors, Screenshot = p.Files diff --git a/TASVideos/wwwroot/js/catalog.js b/TASVideos/wwwroot/js/catalog.js index f82bfbfa0..2750303d8 100644 --- a/TASVideos/wwwroot/js/catalog.js +++ b/TASVideos/wwwroot/js/catalog.js @@ -3,12 +3,15 @@ frameRateElemId, gameElemId, versionElemId, + gameGoalElemId, returnUrl) { const systemModel = document.getElementById(systemElemId); const frameRateModel = document.getElementById(frameRateElemId); const gameModel = document.getElementById(gameElemId); const versionModel = document.getElementById(versionElemId); - const createVersionBtn = document.getElementById("create-version"); + const createVersionBtn = document.getElementById('create-version'); + const gameGoalModel = document.getElementById(gameGoalElemId); + const gameGoalBtn = document.getElementById('create-goal'); systemModel.onchange = function () { if (this.value) { @@ -24,6 +27,7 @@ } else { clearDropdown(gameElemId); clearDropdown(frameRateElemId); + clearDropdown(gameGoalElemId); } clearDropdown(versionElemId); @@ -33,14 +37,22 @@ if (this.value) { createVersionBtn.removeAttribute('disabled'); createVersionBtn.classList.remove('disabled'); + gameGoalBtn.removeAttribute('disabled'); + gameGoalBtn.classList.remove('disabled'); fetch(`/Games/List/VersionDropDownForGame?includeEmpty=true&gameId=${gameModel.value}&systemId=${systemModel.value}`) .then(handleFetchErrors) .then(r => r.text()) .then(t => versionModel.innerHTML = t); + console.log('getting game goals', gameModel.value); + fetch(`/Games/List/GameGoalDropDownForGame?includeEmpty=false&gameId=${gameModel.value}`) + .then(handleFetchErrors) + .then(r => r.text()) + .then(t => gameGoalModel.innerHTML = t); } else { createVersionBtn.classList.add('disabled'); createVersionBtn.setAttribute('disabled', 'disabled'); clearDropdown(versionElemId); + clearDropdown(gameGoalElemId); } } @@ -51,4 +63,8 @@ document.getElementById('create-game').onclick = function () { document.location = `/Games/Edit?returnUrl=${returnUrl}`; } + + gameGoalBtn.onclick = function () { + document.location = `/Games/${gameModel.value}/Goals/List?goalToEdit=${gameGoalModel.value}&returnUrl=${returnUrl}`; + } } \ No newline at end of file diff --git a/tests/TASVideos.Core.Tests/Services/PublicationHistoryTests.cs b/tests/TASVideos.Core.Tests/Services/PublicationHistoryTests.cs index c04ebc501..cb8859cd0 100644 --- a/tests/TASVideos.Core.Tests/Services/PublicationHistoryTests.cs +++ b/tests/TASVideos.Core.Tests/Services/PublicationHistoryTests.cs @@ -20,7 +20,7 @@ public class PublicationHistoryTests Id = 1, GameId = Smb.Id, Title = "Smb in less than 5 minutes", - Branch = "Warps", + GameGoal = new GameGoal { DisplayName = "Warps" }, PublicationClass = PublicationClass }; @@ -29,7 +29,7 @@ public class PublicationHistoryTests Id = 2, GameId = Smb.Id, Title = "Smb in 5 minutes", - Branch = "Warps", + GameGoal = new GameGoal { DisplayName = "Warps" }, ObsoletedById = SmbWarps.Id, PublicationClass = PublicationClass }; @@ -39,17 +39,17 @@ public class PublicationHistoryTests Id = 3, GameId = Smb.Id, Title = "Smb in 5.5 minutes", - Branch = "Warps", + GameGoal = new GameGoal { DisplayName = "Warps" }, ObsoletedById = SmbWarpsObsolete.Id, PublicationClass = PublicationClass }; - private static Publication SmbWarpsObsoleteBranch => new() + private static Publication SmbWarpsObsoleteGoal => new() { Id = 4, GameId = Smb.Id, Title = "Smb in 6 minutes without using glitches", - Branch = "Warps", + GameGoal = new GameGoal { DisplayName = "Warps" }, ObsoletedById = SmbWarps.Id, PublicationClass = PublicationClass }; @@ -59,7 +59,7 @@ public class PublicationHistoryTests Id = 10, GameId = Smb.Id, Title = "Smb in about 20 minutes", - Branch = "No Warps", + GameGoal = new GameGoal { DisplayName = "No Warps" }, PublicationClass = PublicationClass }; @@ -68,7 +68,7 @@ public class PublicationHistoryTests Id = 20, GameId = Smb2j.Id, Title = "Smb2j in about 8 minutes", - Branch = "Warps", + GameGoal = new GameGoal { DisplayName = "Warps" }, PublicationClass = PublicationClass }; @@ -99,15 +99,15 @@ public async Task ForGame_GameIdMatches() } [TestMethod] - public async Task ForGame_NoPublications_BranchesEmpty() + public async Task ForGame_NoPublications_GoalsEmpty() { _db.Add(Smb); await _db.SaveChangesAsync(); var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); - Assert.AreEqual(0, actual.Branches.Count()); + Assert.IsNotNull(actual.Goals); + Assert.AreEqual(0, actual.Goals.Count()); } [TestMethod] @@ -125,11 +125,11 @@ public async Task ForGame_FiltersByGame() var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); Assert.AreEqual(Smb.Id, actual.GameId); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(1, branchList.Count); - Assert.AreEqual(SmbWarps.Id, branchList.Single().Id); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(1, goalList.Count); + Assert.AreEqual(SmbWarps.Id, goalList.Single().Id); } [TestMethod] @@ -141,15 +141,15 @@ public async Task ForGame_SinglePublication_ResultMatches() var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(1, branchList.Count); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(1, goalList.Count); - var movie = branchList.Single(); + var movie = goalList.Single(); Assert.AreEqual(SmbWarps.Id, movie.Id); Assert.AreEqual(SmbWarps.Title, movie.Title); - Assert.AreEqual(SmbWarps.Branch, movie.Branch); + Assert.AreEqual(SmbWarps.GameGoal!.DisplayName, movie.Goal); } [TestMethod] @@ -161,18 +161,18 @@ public async Task ForGame_SinglePublication_NoObsolete_EmptyList() var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(1, branchList.Count); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(1, goalList.Count); - var movie = branchList.Single(); + var movie = goalList.Single(); Assert.IsNotNull(movie.Obsoletes); Assert.AreEqual(0, movie.Obsoletes.Count()); } [TestMethod] - public async Task ForGame_MultiBranch_ResultMatches() + public async Task ForGame_MultiGoal_ResultMatches() { _db.Add(Smb); _db.Add(SmbWarps); @@ -181,30 +181,30 @@ public async Task ForGame_MultiBranch_ResultMatches() var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(2, branchList.Count); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(2, goalList.Count); - Assert.AreEqual(1, branchList.Count(b => b.Branch == SmbWarps.Branch)); - Assert.AreEqual(1, branchList.Count(b => b.Branch == SmbWarpless.Branch)); + Assert.AreEqual(1, goalList.Count(b => b.Goal == SmbWarps.GameGoal!.DisplayName)); + Assert.AreEqual(1, goalList.Count(b => b.Goal == SmbWarpless.GameGoal!.DisplayName)); } [TestMethod] - public async Task ForGame_ObsoleteBranch_NotParentNode() + public async Task ForGame_ObsoleteGoal_NotParentNode() { _db.Add(Smb); _db.Add(SmbWarps); - _db.Add(SmbWarpsObsoleteBranch); + _db.Add(SmbWarpsObsoleteGoal); await _db.SaveChangesAsync(); var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(1, branchList.Count); - Assert.AreEqual(SmbWarps.Branch, branchList.Single().Branch); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(1, goalList.Count); + Assert.AreEqual(SmbWarps.GameGoal!.DisplayName, goalList.Single().Goal); } [TestMethod] @@ -217,12 +217,12 @@ public async Task ForGame_ReturnsObsolete() var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(1, branchList.Count); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(1, goalList.Count); - var currentPub = branchList.Single(); + var currentPub = goalList.Single(); Assert.AreEqual(SmbWarps.Id, currentPub.Id); Assert.IsNotNull(currentPub.Obsoletes); @@ -238,24 +238,24 @@ public async Task ForGame_OnePubWithMultipleObsoletions() _db.Add(Smb); _db.Add(SmbWarps); _db.Add(SmbWarpsObsolete); - _db.Add(SmbWarpsObsoleteBranch); + _db.Add(SmbWarpsObsoleteGoal); await _db.SaveChangesAsync(); var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(1, branchList.Count); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(1, goalList.Count); - var currentPub = branchList.Single(); + var currentPub = goalList.Single(); Assert.AreEqual(SmbWarps.Id, currentPub.Id); Assert.IsNotNull(currentPub.Obsoletes); var obsoletes = currentPub.Obsoletes.ToList(); Assert.AreEqual(2, obsoletes.Count); Assert.AreEqual(1, obsoletes.Count(o => o.Id == SmbWarpsObsolete.Id)); - Assert.AreEqual(1, obsoletes.Count(o => o.Id == SmbWarpsObsoleteBranch.Id)); + Assert.AreEqual(1, obsoletes.Count(o => o.Id == SmbWarpsObsoleteGoal.Id)); } [TestMethod] @@ -269,12 +269,12 @@ public async Task ForGame_ObsoletionChain() var actual = await _publicationHistory.ForGame(Smb.Id); Assert.IsNotNull(actual); - Assert.IsNotNull(actual.Branches); + Assert.IsNotNull(actual.Goals); - var branchList = actual.Branches.ToList(); - Assert.AreEqual(1, branchList.Count); + var goalList = actual.Goals.ToList(); + Assert.AreEqual(1, goalList.Count); - var currentPub = branchList.Single(); + var currentPub = goalList.Single(); Assert.AreEqual(SmbWarps.Id, currentPub.Id); Assert.IsNotNull(currentPub.Obsoletes);