From e2c15a905fc3f3c3b951cc45a0a910f2a8b19eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0ng=20Nam?= Date: Wed, 3 Jun 2026 09:54:26 +0700 Subject: [PATCH] feat : Fix for migration and course final --- .../Entities/CurriculumSection.cs | 7 +- ...axCapacityAndCurrentEnrollment.Designer.cs | 1493 +++++++++++++++++ ...0622_AddMaxCapacityAndCurrentEnrollment.cs | 45 + 3 files changed, 1543 insertions(+), 2 deletions(-) create mode 100644 backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.Designer.cs create mode 100644 backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.cs diff --git a/backend/Src/PIED_LMS.Domain/Entities/CurriculumSection.cs b/backend/Src/PIED_LMS.Domain/Entities/CurriculumSection.cs index 2862e3de..91bc1eb9 100644 --- a/backend/Src/PIED_LMS.Domain/Entities/CurriculumSection.cs +++ b/backend/Src/PIED_LMS.Domain/Entities/CurriculumSection.cs @@ -1,10 +1,12 @@ +using System.Text.Json.Serialization; + namespace PIED_LMS.Domain.Entities; public class CurriculumSection { public string Title { get; private set; } public string Summary { get; private set; } - public IReadOnlyList Content { get; private set; } + public List Content { get; private set; } // Required for EF Core serialization/deserialization or default instantiation private CurriculumSection() @@ -14,6 +16,7 @@ private CurriculumSection() Content = null!; } + [JsonConstructor] public CurriculumSection(string title, string summary, List content) { if (string.IsNullOrWhiteSpace(title)) @@ -27,6 +30,6 @@ public CurriculumSection(string title, string summary, List content) Title = title; Summary = summary; - Content = new List(content).AsReadOnly(); + Content = new List(content); } } diff --git a/backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.Designer.cs b/backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.Designer.cs new file mode 100644 index 00000000..f60340de --- /dev/null +++ b/backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.Designer.cs @@ -0,0 +1,1493 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PIED_LMS.Persistence; + +#nullable disable + +namespace PIED_LMS.Persistence.Migrations +{ + [DbContext(typeof(PiedLmsDbContext))] + [Migration("20260603020622_AddMaxCapacityAndCurrentEnrollment")] + partial class AddMaxCapacityAndCurrentEnrollment + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "uuid-ossp"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text") + .HasColumnName("claim_type"); + + b.Property("ClaimValue") + .HasColumnType("text") + .HasColumnName("claim_value"); + + b.Property("RoleId") + .HasColumnType("uuid") + .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("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text") + .HasColumnName("claim_type"); + + b.Property("ClaimValue") + .HasColumnType("text") + .HasColumnName("claim_value"); + + b.Property("UserId") + .HasColumnType("uuid") + .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("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text") + .HasColumnName("login_provider"); + + b.Property("ProviderKey") + .HasColumnType("text") + .HasColumnName("provider_key"); + + b.Property("ProviderDisplayName") + .HasColumnType("text") + .HasColumnName("provider_display_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .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("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("RoleId") + .HasColumnType("uuid") + .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("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("LoginProvider") + .HasColumnType("text") + .HasColumnName("login_provider"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Value") + .HasColumnType("text") + .HasColumnName("value"); + + b.HasKey("UserId", "LoginProvider", "Name") + .HasName("pk_user_tokens"); + + b.ToTable("user_tokens", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text") + .HasColumnName("concurrency_stamp"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("description"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("name"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("normalized_name"); + + b.HasKey("Id") + .HasName("pk_roles"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AccessFailedCount") + .HasColumnType("integer") + .HasColumnName("access_failed_count"); + + b.Property("Bio") + .HasColumnType("text") + .HasColumnName("bio"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text") + .HasColumnName("concurrency_stamp"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("email"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean") + .HasColumnName("email_confirmed"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("first_name"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("last_name"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean") + .HasColumnName("lockout_enabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone") + .HasColumnName("lockout_end"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("normalized_email"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("normalized_user_name"); + + b.Property("PasswordHash") + .HasColumnType("text") + .HasColumnName("password_hash"); + + b.Property("PhoneNumber") + .HasColumnType("text") + .HasColumnName("phone_number"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean") + .HasColumnName("phone_number_confirmed"); + + b.Property("ProfilePictureUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("profile_picture_url"); + + b.Property("SecurityStamp") + .HasColumnType("text") + .HasColumnName("security_stamp"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean") + .HasColumnName("two_factor_enabled"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("pk_users"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.CodeSubmission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text") + .HasColumnName("code"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("ExamId") + .HasColumnType("uuid") + .HasColumnName("exam_id"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("language"); + + b.Property("Memory") + .HasColumnType("double precision") + .HasColumnName("memory"); + + b.Property("PassedTestCases") + .HasColumnType("integer") + .HasColumnName("passed_test_cases"); + + b.Property("Runtime") + .HasColumnType("double precision") + .HasColumnName("runtime"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("status"); + + b.Property("StudentId") + .HasColumnType("uuid") + .HasColumnName("student_id"); + + b.Property("TotalTestCases") + .HasColumnType("integer") + .HasColumnName("total_test_cases"); + + b.HasKey("Id") + .HasName("pk_code_submissions"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("ix_code_submissions_created_at"); + + b.HasIndex("ExamId") + .HasDatabaseName("ix_code_submissions_exam_id"); + + b.HasIndex("StudentId") + .HasDatabaseName("ix_code_submissions_student_id"); + + b.ToTable("code_submissions", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CurrentEnrollment") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("current_enrollment"); + + b.Property("Curriculum") + .IsRequired() + .HasColumnType("text") + .HasColumnName("curriculum"); + + b.Property("Description") + .HasMaxLength(5000) + .HasColumnType("character varying(5000)") + .HasColumnName("description"); + + b.Property("Duration") + .HasColumnType("integer") + .HasColumnName("duration"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("end_date"); + + b.Property("Insight") + .HasColumnType("text") + .HasColumnName("insight"); + + b.Property("MaxCapacity") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("max_capacity"); + + b.Property("Price") + .HasColumnType("text") + .HasColumnName("price"); + + b.Property("Seats") + .HasColumnType("text") + .HasColumnName("seats"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("slug"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_date"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text") + .HasColumnName("status"); + + b.Property("Tags") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("tags"); + + b.Property("ThumbnailPath") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("thumbnail_path"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Value") + .HasColumnType("integer") + .HasColumnName("value"); + + b.HasKey("Id") + .HasName("pk_courses"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("ix_courses_slug"); + + b.ToTable("courses", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Enrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CourseId") + .HasColumnType("uuid") + .HasColumnName("course_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Notes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("notes"); + + b.Property("PaymentProofKey") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("payment_proof_key"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_enrollments"); + + b.HasIndex("CourseId") + .HasDatabaseName("ix_enrollments_course_id"); + + b.HasIndex("UserId", "CourseId") + .HasDatabaseName("ix_enrollments_user_id_course_id"); + + b.ToTable("enrollments", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.EnrollmentHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ChangeReason") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("change_reason"); + + b.Property("ChangedBy") + .HasColumnType("uuid") + .HasColumnName("changed_by"); + + b.Property("EnrollmentId") + .HasColumnType("uuid") + .HasColumnName("enrollment_id"); + + b.Property("NewStatus") + .IsRequired() + .HasColumnType("text") + .HasColumnName("new_status"); + + b.Property("OldStatus") + .HasColumnType("text") + .HasColumnName("old_status"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp"); + + b.HasKey("Id") + .HasName("pk_enrollment_histories"); + + b.HasIndex("ChangedBy") + .HasDatabaseName("ix_enrollment_histories_changed_by"); + + b.HasIndex("EnrollmentId") + .HasDatabaseName("ix_enrollment_histories_enrollment_id"); + + b.ToTable("enrollment_histories", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Exam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("description"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("PassingMarks") + .HasColumnType("integer") + .HasColumnName("passing_marks"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("title"); + + b.Property("TotalMarks") + .HasColumnType("integer") + .HasColumnName("total_marks"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_exams"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("ix_exams_created_by"); + + b.HasIndex("IsDeleted") + .HasDatabaseName("ix_exams_is_deleted"); + + b.ToTable("exams", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamParticipation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AnswersJson") + .HasColumnType("text") + .HasColumnName("answers_json"); + + b.Property("Deadline") + .HasColumnType("timestamp with time zone") + .HasColumnName("deadline"); + + b.Property("ExamId") + .HasColumnType("uuid") + .HasColumnName("exam_id"); + + b.Property("ExamRoomId") + .HasColumnType("uuid") + .HasColumnName("exam_room_id"); + + b.Property("IsCompleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_completed"); + + b.Property("Score") + .HasColumnType("integer") + .HasColumnName("score"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_at"); + + b.Property("StudentId") + .HasColumnType("uuid") + .HasColumnName("student_id"); + + b.Property("SubmittedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("submitted_at"); + + b.HasKey("Id") + .HasName("pk_exam_participations"); + + b.HasIndex("StudentId") + .HasDatabaseName("ix_exam_participations_student_id"); + + b.HasIndex("ExamId", "StudentId") + .HasDatabaseName("ix_exam_participations_exam_id_student_id"); + + b.HasIndex("ExamRoomId", "StudentId") + .HasDatabaseName("ix_exam_participations_exam_room_id_student_id"); + + b.ToTable("exam_participations", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("description"); + + b.Property("DurationInMinutes") + .HasColumnType("integer") + .HasColumnName("duration_in_minutes"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("end_time"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("RoomCode") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)") + .HasColumnName("room_code"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_exam_rooms"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("ix_exam_rooms_created_by"); + + b.HasIndex("EndTime") + .HasDatabaseName("ix_exam_rooms_end_time"); + + b.HasIndex("IsDeleted") + .HasDatabaseName("ix_exam_rooms_is_deleted"); + + b.HasIndex("RoomCode") + .IsUnique() + .HasDatabaseName("ix_exam_rooms_room_code"); + + b.HasIndex("StartTime") + .HasDatabaseName("ix_exam_rooms_start_time"); + + b.ToTable("exam_rooms", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamRoomEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("EmailSent") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("email_sent"); + + b.Property("EmailSentAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("email_sent_at"); + + b.Property("EnrolledAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("enrolled_at"); + + b.Property("ExamRoomId") + .HasColumnType("uuid") + .HasColumnName("exam_room_id"); + + b.Property("StudentId") + .HasColumnType("uuid") + .HasColumnName("student_id"); + + b.HasKey("Id") + .HasName("pk_exam_room_enrollments"); + + b.HasIndex("StudentId") + .HasDatabaseName("ix_exam_room_enrollments_student_id"); + + b.HasIndex("ExamRoomId", "StudentId") + .IsUnique() + .HasDatabaseName("ix_exam_room_enrollments_exam_room_id_student_id"); + + b.ToTable("exam_room_enrollments", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamRoomExam", b => + { + b.Property("ExamRoomId") + .HasColumnType("uuid") + .HasColumnName("exam_room_id"); + + b.Property("ExamId") + .HasColumnType("uuid") + .HasColumnName("exam_id"); + + b.Property("AssignedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("assigned_at"); + + b.HasKey("ExamRoomId", "ExamId") + .HasName("pk_exam_room_exams"); + + b.HasIndex("ExamId") + .HasDatabaseName("ix_exam_room_exams_exam_id"); + + b.ToTable("exam_room_exams", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Question", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasColumnName("content"); + + b.Property("Explanation") + .HasColumnType("text") + .HasColumnName("explanation"); + + b.Property("IsHidden") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_hidden"); + + b.Property("Level") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1) + .HasColumnName("level"); + + b.Property("QuestionType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("question_type"); + + b.Property("QuizId") + .HasColumnType("integer") + .HasColumnName("quiz_id"); + + b.Property("Score") + .ValueGeneratedOnAdd() + .HasColumnType("double precision") + .HasDefaultValue(0.0) + .HasColumnName("score"); + + b.HasKey("Id") + .HasName("pk_questions"); + + b.HasIndex("QuizId") + .HasDatabaseName("ix_questions_quiz_id"); + + b.ToTable("questions", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.QuestionAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasColumnName("content"); + + b.Property("IsCorrect") + .HasColumnType("boolean") + .HasColumnName("is_correct"); + + b.Property("QuestionId") + .HasColumnType("integer") + .HasColumnName("question_id"); + + b.HasKey("Id") + .HasName("pk_question_answers"); + + b.HasIndex("QuestionId") + .HasDatabaseName("ix_question_answers_question_id"); + + b.ToTable("question_answers", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.QuestionQuiz", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("description"); + + b.Property("IsHidden") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_hidden"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_published"); + + b.Property("Level") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1) + .HasColumnName("level"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_question_quizs"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_question_quizs_user_id"); + + b.ToTable("question_quizs", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.TestCase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ExamId") + .HasColumnType("uuid") + .HasColumnName("exam_id"); + + b.Property("Index") + .HasColumnType("integer") + .HasColumnName("index"); + + b.Property("InputPath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("input_path"); + + b.Property("IsHidden") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_hidden"); + + b.Property("OutputPath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("output_path"); + + b.HasKey("Id") + .HasName("pk_test_cases"); + + b.HasIndex("ExamId", "Index") + .IsUnique() + .HasDatabaseName("ix_test_cases_exam_id_index"); + + b.ToTable("test_cases", (string)null); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.TestRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("description"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("end_time"); + + b.Property("JoinCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("join_code"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_test_room"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("ix_test_room_created_by"); + + b.HasIndex("JoinCode") + .IsUnique() + .HasDatabaseName("ix_test_room_join_code"); + + b.ToTable("test_room", null, t => + { + t.HasCheckConstraint("CK_TestRoom_EndTime_After_StartTime", "\"end_time\" > \"start_time\""); + }); + }); + + modelBuilder.Entity("course_mentors", b => + { + b.Property("course_id") + .HasColumnType("uuid") + .HasColumnName("course_id"); + + b.Property("user_id") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("course_id", "user_id") + .HasName("pk_course_mentors"); + + b.HasIndex("user_id") + .HasDatabaseName("ix_course_mentors_user_id"); + + b.ToTable("course_mentors", (string)null); + }); + + modelBuilder.Entity("course_prerequisites", b => + { + b.Property("course_id") + .HasColumnType("uuid") + .HasColumnName("course_id"); + + b.Property("prerequisite_course_id") + .HasColumnType("uuid") + .HasColumnName("prerequisite_course_id"); + + b.HasKey("course_id", "prerequisite_course_id") + .HasName("pk_course_prerequisites"); + + b.HasIndex("prerequisite_course_id") + .HasDatabaseName("ix_course_prerequisites_prerequisite_course_id"); + + b.ToTable("course_prerequisites", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_role_claims_roles_role_id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_claims_users_user_id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_logins_users_user_id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_roles_roles_role_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_roles_users_user_id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_tokens_users_user_id"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.CodeSubmission", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Exam", "Exam") + .WithMany() + .HasForeignKey("ExamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_code_submissions_exam_exam_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_code_submissions_users_student_id"); + + b.Navigation("Exam"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Enrollment", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Course", "Course") + .WithMany("Enrollments") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_enrollments_courses_course_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_enrollments_users_user_id"); + + b.Navigation("Course"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.EnrollmentHistory", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "ChangedByUser") + .WithMany() + .HasForeignKey("ChangedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_enrollment_histories_users_changed_by"); + + b.HasOne("PIED_LMS.Domain.Entities.Enrollment", "Enrollment") + .WithMany("Histories") + .HasForeignKey("EnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_enrollment_histories_enrollments_enrollment_id"); + + b.Navigation("ChangedByUser"); + + b.Navigation("Enrollment"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Exam", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_exams_users_created_by"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamParticipation", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Exam", "Exam") + .WithMany() + .HasForeignKey("ExamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_exam_participations_exams_exam_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ExamRoom", "ExamRoom") + .WithMany("Participations") + .HasForeignKey("ExamRoomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_exam_participations_exam_room_exam_room_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_exam_participations_users_student_id"); + + b.Navigation("Exam"); + + b.Navigation("ExamRoom"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamRoom", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_exam_rooms_users_created_by"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamRoomEnrollment", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ExamRoom", "ExamRoom") + .WithMany("Enrollments") + .HasForeignKey("ExamRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_exam_room_enrollments_exam_room_exam_room_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_exam_room_enrollments_users_student_id"); + + b.Navigation("ExamRoom"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamRoomExam", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Exam", "Exam") + .WithMany("ExamRoomExams") + .HasForeignKey("ExamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_exam_room_exams_exams_exam_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ExamRoom", "ExamRoom") + .WithMany("ExamRoomExams") + .HasForeignKey("ExamRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_exam_room_exams_exam_rooms_exam_room_id"); + + b.Navigation("Exam"); + + b.Navigation("ExamRoom"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Question", b => + { + b.HasOne("PIED_LMS.Domain.Entities.QuestionQuiz", "Quizlet") + .WithMany("Questions") + .HasForeignKey("QuizId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_questions_question_quiz_quiz_id"); + + b.Navigation("Quizlet"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.QuestionAnswer", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Question", "Question") + .WithMany("Answers") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_question_answers_question_question_id"); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.QuestionQuiz", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_question_quizs_users_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.TestCase", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Exam", "Exam") + .WithMany("TestCases") + .HasForeignKey("ExamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_test_cases_exams_exam_id"); + + b.Navigation("Exam"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.TestRoom", b => + { + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_test_room_users_created_by"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("course_mentors", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Course", null) + .WithMany() + .HasForeignKey("course_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_course_mentors_courses_course_id"); + + b.HasOne("PIED_LMS.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_course_mentors_users_user_id"); + }); + + modelBuilder.Entity("course_prerequisites", b => + { + b.HasOne("PIED_LMS.Domain.Entities.Course", null) + .WithMany() + .HasForeignKey("course_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_course_prerequisites_courses_course_id"); + + b.HasOne("PIED_LMS.Domain.Entities.Course", null) + .WithMany() + .HasForeignKey("prerequisite_course_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_course_prerequisites_courses_prerequisite_course_id"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Course", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Enrollment", b => + { + b.Navigation("Histories"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Exam", b => + { + b.Navigation("ExamRoomExams"); + + b.Navigation("TestCases"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.ExamRoom", b => + { + b.Navigation("Enrollments"); + + b.Navigation("ExamRoomExams"); + + b.Navigation("Participations"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.Question", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("PIED_LMS.Domain.Entities.QuestionQuiz", b => + { + b.Navigation("Questions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.cs b/backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.cs new file mode 100644 index 00000000..ff372884 --- /dev/null +++ b/backend/Src/PIED_LMS.Persistence/Migrations/20260603020622_AddMaxCapacityAndCurrentEnrollment.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PIED_LMS.Persistence.Migrations; + +/// +public partial class AddMaxCapacityAndCurrentEnrollment : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Use IF NOT EXISTS to handle databases where these columns may already exist + migrationBuilder.Sql(""" + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'courses' AND column_name = 'current_enrollment' + ) THEN + ALTER TABLE courses ADD current_enrollment integer NOT NULL DEFAULT 0; + END IF; + + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'courses' AND column_name = 'max_capacity' + ) THEN + ALTER TABLE courses ADD max_capacity integer NOT NULL DEFAULT 0; + END IF; + END $$; + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "current_enrollment", + table: "courses"); + + migrationBuilder.DropColumn( + name: "max_capacity", + table: "courses"); + } +}