Feat: Add Project and ProjectTask entities
Some checks failed
build / build (push) Failing after 28s
SonarQube Scan / SonarQube Trigger (push) Successful in 33s

This commit is contained in:
Namu
2025-12-27 17:54:07 +01:00
parent 0327d70657
commit acea39fb6b
11 changed files with 813 additions and 2 deletions

View File

@@ -8,5 +8,7 @@ namespace WorkManagementTool.Data
{
public DbSet<Homework> Homeworks { get; set; } = null!;
public DbSet<SchoolSubject> SchoolSubjects { get; set; } = null!;
public DbSet<Project> Projects { get; set; } = null!;
public DbSet<ProjectTask> ProjectTasks { get; set; } = null!;
}
}

View File

@@ -0,0 +1,10 @@
namespace WorkManagementTool.Data.Entities
{
public class Project
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public List<ProjectTask> Tasks { get; set; } = new();
}
}

View File

@@ -0,0 +1,13 @@
namespace WorkManagementTool.Data.Entities
{
public class ProjectTask
{
public int Id { get; set; }
public string Title { get; set; } = null!;
public string? Description { get; set; }
public DateTime DueDate { get; set; }
public bool IsCompleted { get; set; }
public List<ProjectTask>? NextTasks { get; set; }
public Project Project { get; set; } = null!;
}
}

View File

@@ -0,0 +1,522 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorkManagementTool.Data;
#nullable disable
namespace WorkManagementTool.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20251227164404_AddProjectAndProjectTask")]
partial class AddProjectAndProjectTask
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey<string>", b =>
{
b.Property<byte[]>("CredentialId")
.HasMaxLength(1024)
.HasColumnType("BLOB");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("CredentialId");
b.HasIndex("UserId");
b.ToTable("AspNetUserPasskeys", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("WorkManagementTool.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Homework", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CreatedById")
.HasColumnType("TEXT");
b.Property<bool>("Deleted")
.HasColumnType("INTEGER");
b.Property<string>("DeliveryMethod")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("SchoolSubjectId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("SchoolSubjectId");
b.ToTable("Homeworks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Projects");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("ProjectId")
.HasColumnType("INTEGER");
b.Property<int?>("ProjectTaskId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProjectId");
b.HasIndex("ProjectTaskId");
b.ToTable("ProjectTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CreatedById")
.HasColumnType("TEXT");
b.Property<bool>("Deleted")
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.ToTable("SchoolSubjects");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("Microsoft.AspNetCore.Identity.IdentityPasskeyData", "Data", b1 =>
{
b1.Property<byte[]>("IdentityUserPasskeyCredentialId");
b1.Property<byte[]>("AttestationObject")
.IsRequired();
b1.Property<byte[]>("ClientDataJson")
.IsRequired();
b1.Property<DateTimeOffset>("CreatedAt");
b1.Property<bool>("IsBackedUp");
b1.Property<bool>("IsBackupEligible");
b1.Property<bool>("IsUserVerified");
b1.Property<string>("Name");
b1.Property<byte[]>("PublicKey")
.IsRequired();
b1.Property<uint>("SignCount");
b1.PrimitiveCollection<string>("Transports");
b1.HasKey("IdentityUserPasskeyCredentialId");
b1.ToTable("AspNetUserPasskeys");
b1.ToJson("Data");
b1.WithOwner()
.HasForeignKey("IdentityUserPasskeyCredentialId");
});
b.Navigation("Data")
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Homework", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.HasOne("WorkManagementTool.Data.Entities.SchoolSubject", "SchoolSubject")
.WithMany("Homeworks")
.HasForeignKey("SchoolSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreatedBy");
b.Navigation("SchoolSubject");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.HasOne("WorkManagementTool.Data.Entities.Project", "Project")
.WithMany("Tasks")
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.Entities.ProjectTask", null)
.WithMany("NextTasks")
.HasForeignKey("ProjectTaskId");
b.Navigation("Project");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.Navigation("CreatedBy");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Navigation("Tasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Navigation("NextTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Navigation("Homeworks");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,78 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace WorkManagementTool.Migrations
{
/// <inheritdoc />
public partial class AddProjectAndProjectTask : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Projects",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Projects", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ProjectTasks",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Title = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: true),
DueDate = table.Column<DateTime>(type: "TEXT", nullable: false),
IsCompleted = table.Column<bool>(type: "INTEGER", nullable: false),
ProjectId = table.Column<int>(type: "INTEGER", nullable: false),
ProjectTaskId = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ProjectTasks", x => x.Id);
table.ForeignKey(
name: "FK_ProjectTasks_ProjectTasks_ProjectTaskId",
column: x => x.ProjectTaskId,
principalTable: "ProjectTasks",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ProjectTasks_Projects_ProjectId",
column: x => x.ProjectId,
principalTable: "Projects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ProjectTasks_ProjectId",
table: "ProjectTasks",
column: "ProjectId");
migrationBuilder.CreateIndex(
name: "IX_ProjectTasks_ProjectTaskId",
table: "ProjectTasks",
column: "ProjectTaskId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ProjectTasks");
migrationBuilder.DropTable(
name: "Projects");
}
}
}

View File

@@ -276,6 +276,58 @@ namespace WorkManagementTool.Migrations
b.ToTable("Homeworks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Projects");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("ProjectId")
.HasColumnType("INTEGER");
b.Property<int?>("ProjectTaskId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProjectId");
b.HasIndex("ProjectTaskId");
b.ToTable("ProjectTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Property<int>("Id")
@@ -297,7 +349,6 @@ namespace WorkManagementTool.Migrations
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT");
b.HasKey("Id");
@@ -414,7 +465,7 @@ namespace WorkManagementTool.Migrations
.HasForeignKey("CreatedById");
b.HasOne("WorkManagementTool.Data.Entities.SchoolSubject", "SchoolSubject")
.WithMany()
.WithMany("Homeworks")
.HasForeignKey("SchoolSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@@ -424,6 +475,21 @@ namespace WorkManagementTool.Migrations
b.Navigation("SchoolSubject");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.HasOne("WorkManagementTool.Data.Entities.Project", "Project")
.WithMany("Tasks")
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.Entities.ProjectTask", null)
.WithMany("NextTasks")
.HasForeignKey("ProjectTaskId");
b.Navigation("Project");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
@@ -432,6 +498,21 @@ namespace WorkManagementTool.Migrations
b.Navigation("CreatedBy");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Navigation("Tasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Navigation("NextTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Navigation("Homeworks");
});
#pragma warning restore 612, 618
}
}

View File

@@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore;
using WorkManagementTool.Data;
using WorkManagementTool.Data.Entities;
namespace WorkManagementTool.Services
{
public class ProjectService
{
private readonly ApplicationDbContext _context;
public ProjectService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<Project>> GetAllProjectsAsync()
{
return await _context.Projects.ToListAsync();
}
public async Task<Project?> GetProjectByIdAsync(int projectId)
{
return await _context.Projects
.Include(p => p.Tasks)
.FirstOrDefaultAsync(p => p.Id == projectId);
}
public async Task AddProjectAsync(Project project)
{
_context.Projects.Add(project);
await _context.SaveChangesAsync();
}
public async Task UpdateProjectAsync(Project project)
{
_context.Projects.Update(project);
await _context.SaveChangesAsync();
}
public async Task DeleteProjectAsync(int projectId)
{
var project = await _context.Projects.FindAsync(projectId);
if (project != null)
{
_context.Projects.Remove(project);
await _context.SaveChangesAsync();
}
}
}
}

View File

@@ -0,0 +1,55 @@
using Microsoft.EntityFrameworkCore;
using WorkManagementTool.Data;
using WorkManagementTool.Data.Entities;
namespace WorkManagementTool.Services
{
public class ProjectTaskService
{
private readonly ApplicationDbContext _context;
public ProjectTaskService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<ProjectTask>> GetAllTasksAsync(int projectId)
{
return await _context.ProjectTasks
.Include(t => t.NextTasks)
.Include(t => t.Project)
.Where(w => w.Project.Id == projectId)
.ToListAsync();
}
public async Task<ProjectTask?> GetTaskByIdAsync(int taskId)
{
return await _context.ProjectTasks
.Include(t => t.NextTasks)
.Include(t => t.Project)
.FirstOrDefaultAsync(t => t.Id == taskId);
}
public async Task AddTaskAsync(ProjectTask task)
{
_context.ProjectTasks.Add(task);
await _context.SaveChangesAsync();
}
public async Task UpdateTaskAsync(ProjectTask task)
{
_context.ProjectTasks.Update(task);
await _context.SaveChangesAsync();
}
public async Task DeleteTaskAsync(int taskId)
{
var task = await _context.ProjectTasks.FindAsync(taskId);
if (task != null)
{
_context.ProjectTasks.Remove(task);
await _context.SaveChangesAsync();
}
}
}
}