diff --git a/.gitignore b/.gitignore
index 4d850f4..1ebc671 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1060,3 +1060,5 @@ coverage*[.json, .xml, .info]
# Additional files built by Visual Studio
# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,csharp,visualstudio,visualstudiocode,vs,vim,rider,intellij+all,intellij+iml,intellij
+
+*.env
\ No newline at end of file
diff --git a/LangLang/App.xaml.cs b/LangLang/App.xaml.cs
index 932f5b6..ffd67fc 100644
--- a/LangLang/App.xaml.cs
+++ b/LangLang/App.xaml.cs
@@ -1,17 +1,68 @@
-using System;
-using System.Collections.Generic;
-using System.Configuration;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
+using LangLang.Composition;
+using LangLang.Domain.RepositoryInterfaces;
+using LangLang.Repositories;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Npgsql;
+using System;
+using System.IO;
using System.Windows;
namespace LangLang
{
- ///
- /// Interaction logic for App.xaml
- ///
public partial class App : Application
{
+ private readonly IHost _host;
+
+ public App()
+ {
+
+ DotNetEnv.Env.Load();
+
+ _host = Host.CreateDefaultBuilder().ConfigureServices((context, services) =>
+ {
+ var host = Environment.GetEnvironmentVariable("HOST");
+ var database = Environment.GetEnvironmentVariable("DATABASE");
+ var username = Environment.GetEnvironmentVariable("USERNAME");
+ var password = Environment.GetEnvironmentVariable("PASSWORD");
+
+ var connectionString = $"Host={host};Database={database};Username={username};Password={password}";
+
+ services.AddDbContext(options =>
+ options.UseNpgsql(connectionString));
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+
+ }).Build();
+
+ Injector.SetServiceProvider(_host.Services as ServiceProvider);
+ ApplyMigrations();
+ }
+
+ private void ApplyMigrations()
+ {
+ using (var scope = _host.Services.CreateScope())
+ {
+ var db = scope.ServiceProvider.GetRequiredService();
+ db.Database.Migrate();
+ }
+ }
+
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ _host.Start();
+ base.OnStartup(e);
+ }
+
+ protected override void OnExit(ExitEventArgs e)
+ {
+ _host.StopAsync().Wait();
+ base.OnExit(e);
+ }
+
}
+
}
\ No newline at end of file
diff --git a/LangLang/BusinessLogic/UseCases/CourseService.cs b/LangLang/BusinessLogic/UseCases/CourseService.cs
index b0ee04e..386634b 100644
--- a/LangLang/BusinessLogic/UseCases/CourseService.cs
+++ b/LangLang/BusinessLogic/UseCases/CourseService.cs
@@ -41,10 +41,11 @@ public List GetLanguages()
return GetAll().Select(course => course.Language).Distinct().ToList();
}
- public void Delete(int id)
+ public void Delete(Course course)
{
- _courses.Delete(id);
+ _courses.Delete(course);
}
+
public void DeleteByTutor(Tutor tutor)
{
foreach (Course course in GetByTutor(tutor.Id))
@@ -60,7 +61,7 @@ public void DeleteByTutor(Tutor tutor)
{
EnrollmentRequestService enrollmentRequestService = new();
enrollmentRequestService.Delete(course);
- Delete(course.Id);
+ Delete(course);
}
}
else
diff --git a/LangLang/BusinessLogic/UseCases/ExamSlotService.cs b/LangLang/BusinessLogic/UseCases/ExamSlotService.cs
index cfe53d3..b52eef0 100644
--- a/LangLang/BusinessLogic/UseCases/ExamSlotService.cs
+++ b/LangLang/BusinessLogic/UseCases/ExamSlotService.cs
@@ -133,7 +133,8 @@ public bool Delete(int id)
if (exam == null) return false;
if (!((exam.TimeSlot.Time - DateTime.Now).TotalDays >= Constants.EXAM_MODIFY_PERIOD)) return false;
- _exams.Delete(id);
+
+ _exams.Delete(exam);
return true;
}
diff --git a/LangLang/BusinessLogic/UseCases/TimeSlotService.cs b/LangLang/BusinessLogic/UseCases/TimeSlotService.cs
new file mode 100644
index 0000000..b32f089
--- /dev/null
+++ b/LangLang/BusinessLogic/UseCases/TimeSlotService.cs
@@ -0,0 +1,47 @@
+using LangLang.Composition;
+using LangLang.Domain.Models;
+using LangLang.Domain.RepositoryInterfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LangLang.BusinessLogic.UseCases
+{
+ public class TimeSlotService
+ {
+
+
+ private ITimeSlotRepository _timeSlot;
+
+
+ public TimeSlotService()
+ {
+ _timeSlot = Injector.CreateInstance();
+ }
+
+ private int GenerateId()
+ {
+ var last = GetAll().LastOrDefault();
+ return last?.Id + 1 ?? 0;
+ }
+
+ public List GetAll()
+ {
+ return _timeSlot.GetAll();
+ }
+
+ public TimeSlot Get(int id)
+ {
+ return _timeSlot.Get(id);
+ }
+
+ public void Add(TimeSlot timeslot)
+ {
+ timeslot.Id = GenerateId();
+ _timeSlot.Add(timeslot);
+ }
+
+ }
+}
diff --git a/LangLang/Configuration/Injector.cs b/LangLang/Configuration/Injector.cs
index 8e8a0c9..24658d5 100644
--- a/LangLang/Configuration/Injector.cs
+++ b/LangLang/Configuration/Injector.cs
@@ -2,11 +2,19 @@
using System;
using LangLang.Domain.RepositoryInterfaces;
using LangLang.Repositories;
+using Microsoft.Extensions.DependencyInjection;
namespace LangLang.Composition
{
public class Injector
{
+
+ private static ServiceProvider _serviceProvider;
+ public static void SetServiceProvider(ServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
private static Dictionary _implementations = new Dictionary
{
{ typeof(IStudentRepository), new StudentRepository() },
@@ -15,10 +23,10 @@ public class Injector
{ typeof(IEnrollmentRequestRepository), new EnrollmentRequestRepository() },
{ typeof(IWithdrawalRequestRepository), new WithdrawalRequestRepository() },
{ typeof(ITutorRatingRepository), new TutorRatingRepository() },
- { typeof(IExamSlotRepository), new ExamSlotRepository() },
+ //{ typeof(IExamSlotRepository), new ExamSlotRepository() },
{ typeof(IExamApplicationRepository), new ExamApplicationRepository() },
{ typeof(IPenaltyPointRepository), new PenaltyPointRepository() },
- { typeof(ICourseRepository), new CourseRepository() },
+ //{ typeof(ICourseRepository), new CourseRepository() },
{ typeof(IGradeRepository), new GradeRepository() },
{ typeof(IExamResultRepository), new ExamResultRepository() },
{ typeof(IEmailRepository), new EmailRepository() }
@@ -32,7 +40,10 @@ public static T CreateInstance()
{
return (T)_implementations[type];
}
-
+ else
+ {
+ return _serviceProvider.GetRequiredService();
+ }
throw new ArgumentException($"No implementation found for type {type}");
}
}
diff --git a/LangLang/Domain/Models/TimeSlot.cs b/LangLang/Domain/Models/TimeSlot.cs
index c1f7398..427407f 100644
--- a/LangLang/Domain/Models/TimeSlot.cs
+++ b/LangLang/Domain/Models/TimeSlot.cs
@@ -5,12 +5,13 @@ namespace LangLang.Domain.Models
{
public class TimeSlot
{
- // NOTE: Adapt as needed during implementation
+ public int Id { get; set; }
public double Duration { get; set; }
public DateTime Time { get; set; }
public TimeSlot(double duration, DateTime time)
- {
+ {
+
Duration = duration;
Time = time;
}
diff --git a/LangLang/Domain/RepositoryInterfaces/ICourseRepository.cs b/LangLang/Domain/RepositoryInterfaces/ICourseRepository.cs
index 1e78d9b..e8fe061 100644
--- a/LangLang/Domain/RepositoryInterfaces/ICourseRepository.cs
+++ b/LangLang/Domain/RepositoryInterfaces/ICourseRepository.cs
@@ -9,8 +9,6 @@ internal interface ICourseRepository
public Course Get(int id);
public void Add(Course course);
public void Update(Course course);
- public void Delete(int id);
- public void Save();
- public Dictionary Load();
+ public void Delete(Course course);
}
}
diff --git a/LangLang/Domain/RepositoryInterfaces/IExamSlotRepository.cs b/LangLang/Domain/RepositoryInterfaces/IExamSlotRepository.cs
index 8f434c9..db87c36 100644
--- a/LangLang/Domain/RepositoryInterfaces/IExamSlotRepository.cs
+++ b/LangLang/Domain/RepositoryInterfaces/IExamSlotRepository.cs
@@ -9,14 +9,7 @@ public interface IExamSlotRepository
public ExamSlot Get(int id);
public List GetExams(Tutor tutor);
public void Add(ExamSlot exam);
- public void Delete(int id);
+ public void Delete(ExamSlot exam);
public void Update(ExamSlot exam);
- public void Save();
- public Dictionary Load();
- /*
- public List Search(List exams, DateTime examDate, string language, LanguageLevel? level);
- public List SearchByTutor(Tutor tutor, DateTime examDate, string language, LanguageLevel? level);
- public List SearchByStudent(AppController appController, Student student, DateTime examDate, string courseLanguage, LanguageLevel? languageLevel);
- */
}
-}
+}
\ No newline at end of file
diff --git a/LangLang/Domain/RepositoryInterfaces/ITimeSLotRepository.cs b/LangLang/Domain/RepositoryInterfaces/ITimeSLotRepository.cs
new file mode 100644
index 0000000..4d2ca1e
--- /dev/null
+++ b/LangLang/Domain/RepositoryInterfaces/ITimeSLotRepository.cs
@@ -0,0 +1,12 @@
+using LangLang.Domain.Models;
+using System.Collections.Generic;
+
+namespace LangLang.Domain.RepositoryInterfaces
+{
+ public interface ITimeSlotRepository
+ {
+ public List GetAll();
+ public TimeSlot Get(int id);
+ public void Add(TimeSlot timeSlot);
+ }
+}
\ No newline at end of file
diff --git a/LangLang/LangLang.csproj b/LangLang/LangLang.csproj
index 6d8734b..d878efb 100644
--- a/LangLang/LangLang.csproj
+++ b/LangLang/LangLang.csproj
@@ -1,23 +1,32 @@
-
+
WinExe
- net6.0-windows
+ net7.0-windows
enable
true
-
-
-
-
+
+
+
+
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
-
+
diff --git a/LangLang/Migrations/20240612152547_Initial.Designer.cs b/LangLang/Migrations/20240612152547_Initial.Designer.cs
new file mode 100644
index 0000000..87ee6e8
--- /dev/null
+++ b/LangLang/Migrations/20240612152547_Initial.Designer.cs
@@ -0,0 +1,179 @@
+//
+using System;
+using LangLang.Repositories;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace LangLang.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ [Migration("20240612152547_Initial")]
+ partial class Initial
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.20")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("LangLang.Domain.Models.Course", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedByDirector")
+ .HasColumnType("boolean");
+
+ b.Property("Days")
+ .IsRequired()
+ .HasColumnType("integer[]");
+
+ b.Property("GratitudeEmailSent")
+ .HasColumnType("boolean");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("MaxStudents")
+ .HasColumnType("integer");
+
+ b.Property("Modifiable")
+ .HasColumnType("boolean");
+
+ b.Property("NumberOfStudents")
+ .HasColumnType("integer");
+
+ b.Property("NumberOfWeeks")
+ .HasColumnType("integer");
+
+ b.Property("Online")
+ .HasColumnType("boolean");
+
+ b.Property("StartDateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TutorId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("Course", (string)null);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.ExamSlot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Applicants")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExamineesNotified")
+ .HasColumnType("boolean");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("MaxStudents")
+ .HasColumnType("integer");
+
+ b.Property("Modifiable")
+ .HasColumnType("boolean");
+
+ b.Property("ResultsGenerated")
+ .HasColumnType("boolean");
+
+ b.Property("TutorId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("ExamSlot", (string)null);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.TimeSlot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CourseId")
+ .HasColumnType("integer");
+
+ b.Property("Duration")
+ .HasColumnType("double precision");
+
+ b.Property("ExamId")
+ .HasColumnType("integer");
+
+ b.Property("Time")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CourseId");
+
+ b.HasIndex("ExamId")
+ .IsUnique();
+
+ b.ToTable("TimeSlot", (string)null);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.TimeSlot", b =>
+ {
+ b.HasOne("LangLang.Domain.Models.Course", null)
+ .WithMany("TimeSlots")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("LangLang.Domain.Models.ExamSlot", null)
+ .WithOne("TimeSlot")
+ .HasForeignKey("LangLang.Domain.Models.TimeSlot", "ExamId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.Course", b =>
+ {
+ b.Navigation("TimeSlots");
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.ExamSlot", b =>
+ {
+ b.Navigation("TimeSlot")
+ .IsRequired();
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/LangLang/Migrations/20240612152547_Initial.cs b/LangLang/Migrations/20240612152547_Initial.cs
new file mode 100644
index 0000000..c461631
--- /dev/null
+++ b/LangLang/Migrations/20240612152547_Initial.cs
@@ -0,0 +1,114 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace LangLang.Migrations
+{
+ ///
+ public partial class Initial : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Course",
+ columns: table => new
+ {
+ Id = table.Column(type: "integer", nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ TutorId = table.Column(type: "integer", nullable: false),
+ Language = table.Column(type: "text", nullable: false),
+ Level = table.Column(type: "integer", nullable: false),
+ NumberOfWeeks = table.Column(type: "integer", nullable: false),
+ Days = table.Column(type: "integer[]", nullable: false),
+ Online = table.Column(type: "boolean", nullable: false),
+ NumberOfStudents = table.Column(type: "integer", nullable: false),
+ MaxStudents = table.Column(type: "integer", nullable: false),
+ StartDateTime = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatedByDirector = table.Column(type: "boolean", nullable: false),
+ Modifiable = table.Column(type: "boolean", nullable: false),
+ GratitudeEmailSent = table.Column(type: "boolean", nullable: false),
+ CreatedAt = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Course", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "ExamSlot",
+ columns: table => new
+ {
+ Id = table.Column(type: "integer", nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ Language = table.Column(type: "text", nullable: false),
+ Level = table.Column(type: "integer", nullable: false),
+ TutorId = table.Column(type: "integer", nullable: false),
+ MaxStudents = table.Column(type: "integer", nullable: false),
+ Applicants = table.Column(type: "integer", nullable: false),
+ Modifiable = table.Column(type: "boolean", nullable: false),
+ ResultsGenerated = table.Column(type: "boolean", nullable: false),
+ ExamineesNotified = table.Column(type: "boolean", nullable: false),
+ CreatedAt = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ExamSlot", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "TimeSlot",
+ columns: table => new
+ {
+ Id = table.Column(type: "integer", nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ Duration = table.Column(type: "double precision", nullable: false),
+ Time = table.Column(type: "timestamp with time zone", nullable: false),
+ CourseId = table.Column(type: "integer", nullable: true),
+ ExamId = table.Column(type: "integer", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_TimeSlot", x => x.Id);
+ table.ForeignKey(
+ name: "FK_TimeSlot_Course_CourseId",
+ column: x => x.CourseId,
+ principalTable: "Course",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_TimeSlot_ExamSlot_ExamId",
+ column: x => x.ExamId,
+ principalTable: "ExamSlot",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_TimeSlot_CourseId",
+ table: "TimeSlot",
+ column: "CourseId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_TimeSlot_ExamId",
+ table: "TimeSlot",
+ column: "ExamId",
+ unique: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "TimeSlot");
+
+ migrationBuilder.DropTable(
+ name: "Course");
+
+ migrationBuilder.DropTable(
+ name: "ExamSlot");
+ }
+ }
+}
diff --git a/LangLang/Migrations/DatabaseContextModelSnapshot.cs b/LangLang/Migrations/DatabaseContextModelSnapshot.cs
new file mode 100644
index 0000000..5094e0f
--- /dev/null
+++ b/LangLang/Migrations/DatabaseContextModelSnapshot.cs
@@ -0,0 +1,176 @@
+//
+using System;
+using LangLang.Repositories;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace LangLang.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ partial class DatabaseContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.20")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("LangLang.Domain.Models.Course", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedByDirector")
+ .HasColumnType("boolean");
+
+ b.Property("Days")
+ .IsRequired()
+ .HasColumnType("integer[]");
+
+ b.Property("GratitudeEmailSent")
+ .HasColumnType("boolean");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("MaxStudents")
+ .HasColumnType("integer");
+
+ b.Property("Modifiable")
+ .HasColumnType("boolean");
+
+ b.Property("NumberOfStudents")
+ .HasColumnType("integer");
+
+ b.Property("NumberOfWeeks")
+ .HasColumnType("integer");
+
+ b.Property("Online")
+ .HasColumnType("boolean");
+
+ b.Property("StartDateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TutorId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("Course", (string)null);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.ExamSlot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Applicants")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExamineesNotified")
+ .HasColumnType("boolean");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("MaxStudents")
+ .HasColumnType("integer");
+
+ b.Property("Modifiable")
+ .HasColumnType("boolean");
+
+ b.Property("ResultsGenerated")
+ .HasColumnType("boolean");
+
+ b.Property("TutorId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("ExamSlot", (string)null);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.TimeSlot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CourseId")
+ .HasColumnType("integer");
+
+ b.Property("Duration")
+ .HasColumnType("double precision");
+
+ b.Property("ExamId")
+ .HasColumnType("integer");
+
+ b.Property("Time")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CourseId");
+
+ b.HasIndex("ExamId")
+ .IsUnique();
+
+ b.ToTable("TimeSlot", (string)null);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.TimeSlot", b =>
+ {
+ b.HasOne("LangLang.Domain.Models.Course", null)
+ .WithMany("TimeSlots")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("LangLang.Domain.Models.ExamSlot", null)
+ .WithOne("TimeSlot")
+ .HasForeignKey("LangLang.Domain.Models.TimeSlot", "ExamId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.Course", b =>
+ {
+ b.Navigation("TimeSlots");
+ });
+
+ modelBuilder.Entity("LangLang.Domain.Models.ExamSlot", b =>
+ {
+ b.Navigation("TimeSlot")
+ .IsRequired();
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/LangLang/Repositories/CourseRepository.cs b/LangLang/Repositories/CourseRepository.cs
index 328697b..4cec4fd 100644
--- a/LangLang/Repositories/CourseRepository.cs
+++ b/LangLang/Repositories/CourseRepository.cs
@@ -1,113 +1,45 @@
-using LangLang.Configuration;
-using LangLang.Domain.Enums;
-using LangLang.Domain.Models;
+using LangLang.Domain.Models;
using LangLang.Domain.RepositoryInterfaces;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
namespace LangLang.Repositories
{
public class CourseRepository : ICourseRepository
{
- private readonly Dictionary _courses;
- private string _filePath = Constants.FILENAME_PREFIX + "courses.csv";
- public CourseRepository()
+ private readonly DatabaseContext _databaseContext;
+
+ public CourseRepository(DatabaseContext context)
{
- _courses = Load();
+ _databaseContext = context;
}
+
public Course Get(int id)
{
- return _courses[id];
- }
- public List GetAll()
- {
- return _courses.Values.ToList();
+ return _databaseContext.Course.Find(id);
}
- public void Save()
+ public List GetAll()
{
- using var writer = new StreamWriter(_filePath);
-
- foreach (var course in GetAll())
- {
- var line = course.ToString();
- writer.WriteLine(line);
- }
- }
-
- public Dictionary Load()
- {
- var courses = new Dictionary();
-
- if (!File.Exists(_filePath)) return courses;
-
- var lines = File.ReadAllLines(_filePath);
- foreach (var line in lines)
- {
- var tokens = line.Split(Constants.DELIMITER);
-
- int id = int.Parse(tokens[0]);
- int tutorId = int.Parse(tokens[1]);
- var language = tokens[2];
- var level = (LanguageLevel)Enum.Parse(typeof(LanguageLevel), tokens[3]);
- int numOfWeeks = int.Parse(tokens[4]);
-
- // Converting from string to list of WeekDays
- string[] days = tokens[5].Split(' ');
- var daysOfWeek = new List();
- foreach (string day in days)
- {
- daysOfWeek.Add((DayOfWeek)Enum.Parse(typeof(DayOfWeek), day));
- }
-
- bool online = bool.Parse(tokens[6]);
- int numOfStud = int.Parse(tokens[7]);
- int maxStud = int.Parse(tokens[8]);
- var startDateTime = DateTime.Parse(tokens[9]);
- bool createdByDirector = bool.Parse(tokens[10]);
- bool modifiable = bool.Parse(tokens[11]);
- bool gratitudeEmailSent = bool.Parse(tokens[12]);
- DateTime createdAt = DateTime.ParseExact(tokens[13], Constants.DATE_TIME_FORMAT, null);
- var course = new Course(id, tutorId, language, level, numOfWeeks, daysOfWeek, online, numOfStud, maxStud, startDateTime, createdByDirector, modifiable, gratitudeEmailSent, createdAt);
-
- courses.Add(course.Id, course);
- }
-
- return courses;
+ return _databaseContext.Course.ToList();
}
public void Add(Course course)
{
- _courses.Add(course.Id, course);
- Save();
+ _databaseContext.Course.Add(course);
+ _databaseContext.SaveChanges();
}
public void Update(Course course)
{
- Course oldCourse = Get(course.Id);
- if (oldCourse == null) return;
-
- oldCourse.Language = course.Language;
- oldCourse.Level = course.Level;
- oldCourse.NumberOfWeeks = course.NumberOfWeeks;
- oldCourse.Days = course.Days;
- oldCourse.Online = course.Online;
- oldCourse.NumberOfStudents = course.NumberOfStudents;
- oldCourse.MaxStudents = course.MaxStudents;
- oldCourse.StartDateTime = course.StartDateTime;
- oldCourse.TutorId = course.TutorId;
- oldCourse.Modifiable = course.Modifiable;
- oldCourse.GratitudeEmailSent = course.GratitudeEmailSent;
-
- Save();
+ _databaseContext.Course.Update(course);
+ _databaseContext.SaveChanges();
}
- public void Delete(int id)
+ public void Delete(Course course)
{
- _courses.Remove(id);
- Save();
+ _databaseContext.Course.Remove(course);
+ _databaseContext.SaveChanges();
}
}
diff --git a/LangLang/Repositories/DatabaseContext.cs b/LangLang/Repositories/DatabaseContext.cs
new file mode 100644
index 0000000..9c132cd
--- /dev/null
+++ b/LangLang/Repositories/DatabaseContext.cs
@@ -0,0 +1,46 @@
+using LangLang.Domain.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace LangLang.Repositories
+{
+ public class DatabaseContext : DbContext
+ {
+ public DatabaseContext(DbContextOptions options) : base(options) { }
+
+ public DbSet TimeSlot { get; set; }
+ public DbSet Course { get; set; }
+ public DbSet ExamSlot { get; set; }
+
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+ // Shadow property for CourseId
+ modelBuilder.Entity()
+ .Property("CourseId");
+
+ // Shadow property for ExamId
+ modelBuilder.Entity()
+ .Property("ExamId");
+
+ // Configure the relationship between TimeSlot and Course
+ modelBuilder.Entity()
+ .HasOne()
+ .WithMany(c => c.TimeSlots)
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ // Configure the relationship between TimeSlot and Exam
+ modelBuilder.Entity()
+ .HasOne()
+ .WithOne(e => e.TimeSlot)
+ .HasForeignKey("ExamId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ modelBuilder.Entity().ToTable("Course");
+ modelBuilder.Entity().ToTable("ExamSlot");
+ modelBuilder.Entity().ToTable("TimeSlot");
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/LangLang/Repositories/ExamSlotRepository.cs b/LangLang/Repositories/ExamSlotRepository.cs
index 3d9caf7..8237352 100644
--- a/LangLang/Repositories/ExamSlotRepository.cs
+++ b/LangLang/Repositories/ExamSlotRepository.cs
@@ -1,138 +1,52 @@
-using LangLang.BusinessLogic.UseCases;
-using LangLang.Configuration;
-using LangLang.Domain.Enums;
-using LangLang.Domain.Models;
+using LangLang.Domain.Models;
using LangLang.Domain.RepositoryInterfaces;
-using System;
+using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
namespace LangLang.Repositories
{
public class ExamSlotRepository : IExamSlotRepository
{
- private readonly Dictionary _exams;
- private const string _filePath = Constants.FILENAME_PREFIX + "examSlots.csv";
+ private readonly DatabaseContext _databaseContext;
- public ExamSlotRepository()
+ public ExamSlotRepository(DatabaseContext context)
{
- _exams = Load();
+ _databaseContext = context;
}
public ExamSlot? Get(int id)
{
- return _exams[id];
+ return _databaseContext.ExamSlot.Find(id);
}
public List GetAll()
{
- return _exams.Values.ToList();
+ return _databaseContext.ExamSlot.ToList();
}
- // Method to get all exam slots by tutor ID
- //function takes tutor id
+
public List GetExams(Tutor tutor)
{
- List exams = new List();
-
- foreach (ExamSlot exam in _exams.Values)
- {
-
- if (tutor.Id == exam.TutorId)
- {
- exams.Add(exam);
- }
- }
-
- return exams;
+ return _databaseContext.ExamSlot.Where(es => es.TutorId == tutor.Id).ToList();
}
- //function takes examslot and adds it to dictionary of examslots
- //function saves changes and returns if adding was successful
public void Add(ExamSlot exam)
{
- _exams.Add(exam.Id, exam);
- Save();
+ _databaseContext.ExamSlot.Add(exam);
+ _databaseContext.SaveChanges();
}
- //function for updating examslot takes new version of examslot and updates existing examslot to be same as new one
- //function saves changes and returns if updating was successful
+
public void Update(ExamSlot exam)
{
- ExamSlot? oldExam = Get(exam.Id);
- if (oldExam == null) return;
-
- oldExam.TutorId = exam.TutorId;
- oldExam.MaxStudents = exam.MaxStudents;
- oldExam.TimeSlot = exam.TimeSlot;
- oldExam.Modifiable = exam.Modifiable;
- oldExam.ResultsGenerated = exam.ResultsGenerated;
- oldExam.Applicants = exam.Applicants;
- oldExam.ExamineesNotified = exam.ExamineesNotified;
-
- Save();
+ _databaseContext.ExamSlot.Update(exam);
+ _databaseContext.SaveChanges();
}
- //function takes id of examslot and removes examslot with that id
- //function saves changes and returns if removing was successful
- public void Delete(int id)
- {
-
- _exams.Remove(id);
- Save();
- }
- public void Save()
+ public void Delete(ExamSlot exam)
{
- var lines = GetAll().Select(exam =>
- {
- return string.Join(Constants.DELIMITER,
- exam.Id.ToString(),
- exam.Language,
- exam.Level.ToString(),
- exam.TutorId.ToString(),
- exam.TimeSlot.ToString(),
- exam.MaxStudents.ToString(),
- exam.Applicants.ToString(),
- exam.Modifiable.ToString(),
- exam.ResultsGenerated.ToString(),
- exam.ExamineesNotified.ToString(),
- exam.CreatedAt.ToString(Constants.DATE_TIME_FORMAT));
- });
-
- File.WriteAllLines(_filePath, lines);
+ _databaseContext.ExamSlot.Remove(exam);
+ _databaseContext.SaveChanges();
}
-
- public Dictionary Load()
- {
- var exams = new Dictionary();
-
- if (!File.Exists(_filePath)) return exams;
-
- var lines = File.ReadAllLines(_filePath);
-
- foreach (var line in lines)
- {
- string[] values = line.Split(Constants.DELIMITER);
-
- int id = int.Parse(values[0]);
- string language = values[1];
- LanguageLevel level = (LanguageLevel)Enum.Parse(typeof(LanguageLevel), values[2]);
- int tutorId = int.Parse(values[3]);
- TimeSlot timeSlot = new(values[4], values[5]);
- int maxStudents = int.Parse(values[6]);
- int applicants = int.Parse(values[7]);
- bool modifiable = bool.Parse(values[8]);
- bool resultsGenerated = bool.Parse(values[9]);
- bool examineesNotified = bool.Parse(values[10]);
- DateTime createdAt = DateTime.ParseExact(values[11], Constants.DATE_TIME_FORMAT, null);
-
- ExamSlot exam = new ExamSlot(id, language, level, timeSlot, maxStudents, tutorId, applicants, modifiable, resultsGenerated, examineesNotified, createdAt);
- exams.Add(id, exam);
-
- }
-
- return exams;
-
- }
}
}
\ No newline at end of file
diff --git a/LangLang/Repositories/TimeSlotRepository.cs b/LangLang/Repositories/TimeSlotRepository.cs
new file mode 100644
index 0000000..03fbfe4
--- /dev/null
+++ b/LangLang/Repositories/TimeSlotRepository.cs
@@ -0,0 +1,32 @@
+using LangLang.Domain.Models;
+using LangLang.Domain.RepositoryInterfaces;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace LangLang.Repositories
+{
+ public class TimeSlotRepository : ITimeSlotRepository
+ {
+ private readonly DatabaseContext _databaseContext;
+
+ public TimeSlotRepository(DatabaseContext context) {
+ _databaseContext = context;
+ }
+
+ public void Add(TimeSlot timeSlot)
+ {
+ _databaseContext.TimeSlot.Add(timeSlot);
+ _databaseContext.SaveChanges();
+ }
+
+ public TimeSlot Get(int id)
+ {
+ return _databaseContext.TimeSlot.Find(id);
+ }
+
+ public List GetAll()
+ {
+ return _databaseContext.TimeSlot.ToList();
+ }
+ }
+}
diff --git a/LangLang/WPF/ViewModels/MainWindowViewModel.cs b/LangLang/WPF/ViewModels/MainWindowViewModel.cs
index c897355..b3d7641 100644
--- a/LangLang/WPF/ViewModels/MainWindowViewModel.cs
+++ b/LangLang/WPF/ViewModels/MainWindowViewModel.cs
@@ -18,7 +18,7 @@ public string Email
{
get { return _email; }
set
- {
+ {
if (value != _email)
{
_email = value;
diff --git a/LangLang/WPF/Views/TutorView/Tabs/Courses.xaml.cs b/LangLang/WPF/Views/TutorView/Tabs/Courses.xaml.cs
index c03d0cd..2c432ae 100644
--- a/LangLang/WPF/Views/TutorView/Tabs/Courses.xaml.cs
+++ b/LangLang/WPF/Views/TutorView/Tabs/Courses.xaml.cs
@@ -83,7 +83,7 @@ private void CourseDeleteBtn_Click(object sender, RoutedEventArgs e)
if (CoursesViewModel.SelectedCourse.ToCourse().CanChange())
{
CourseService courseService = new();
- courseService.Delete(CoursesViewModel.SelectedCourse.Id);
+ courseService.Delete(CoursesViewModel.SelectedCourse.ToCourse());
CoursesViewModel.Update();
MessageBox.Show("The course has been successfully deleted.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
}
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..8960f9e
--- /dev/null
+++ b/global.json
@@ -0,0 +1,5 @@
+{
+ "sdk": {
+ "version": "7.0.304"
+ }
+}
\ No newline at end of file