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