From 78c105f309492e815e6001acb85673b1a69693e2 Mon Sep 17 00:00:00 2001
From: esantangelo <enzo020895@gmail.com>
Date: Mon, 30 Nov 2020 21:10:53 -0300
Subject: [PATCH] add grade entity

---
 .../Controllers/ActivityController.cs         |  60 ++
 Tsi1.Api/Tsi1.BusinessLayer/Dtos/GradeDto.cs  |  16 +
 .../Helpers/ErrorMessages.cs                  |   2 +
 .../Helpers/MappingProfile.cs                 |   2 +
 .../Interfaces/IActivityService.cs            |   4 +
 .../Services/ActivityService.cs               | 223 ++++-
 Tsi1.Api/Tsi1.DataLayer/Entities/Activity.cs  |   3 +
 Tsi1.Api/Tsi1.DataLayer/Entities/Grade.cs     |  21 +
 Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs   |   3 +
 .../EntityConfiguration/GradeConfiguration.cs |  29 +
 ...0201130004426_add-grade-entity.Designer.cs | 941 ++++++++++++++++++
 .../20201130004426_add-grade-entity.cs        |  48 +
 .../Migrations/Tsi1ContextModelSnapshot.cs    |  36 +
 Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs        |   3 +
 14 files changed, 1389 insertions(+), 2 deletions(-)
 create mode 100644 Tsi1.Api/Tsi1.BusinessLayer/Dtos/GradeDto.cs
 create mode 100644 Tsi1.Api/Tsi1.DataLayer/Entities/Grade.cs
 create mode 100644 Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/GradeConfiguration.cs
 create mode 100644 Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.Designer.cs
 create mode 100644 Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.cs

diff --git a/Tsi1.Api/Tsi1.Api/Controllers/ActivityController.cs b/Tsi1.Api/Tsi1.Api/Controllers/ActivityController.cs
index d859323..6987480 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/ActivityController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/ActivityController.cs
@@ -80,5 +80,65 @@ namespace Tsi1.Api.Controllers
 
             return Ok();
         }
+
+        [Authorize(Roles = UserTypes.Professor)]
+        [HttpPut("AddOrModifyGrade")]
+        public async Task<IActionResult> AddOrModifyGrade(GradeDto gradeDto)
+        {
+            var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+            var result = await _activityService.AddOrModifyGrade(gradeDto, userId);
+
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok(result.Data);
+        }
+
+        [Authorize(Roles = UserTypes.Professor)]
+        [HttpDelete("DeleteGrade/{activityId}/{userId}")]
+        public async Task<IActionResult> DeleteGrade(int activityId, int userId)
+        {
+            var myUserId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+            var result = await _activityService.DeleteGrade(myUserId, activityId, userId);
+
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok();
+        }
+
+        [Authorize(Roles = UserTypes.Student)]
+        [HttpGet("GetMyGrades/{activityId}")]
+        public async Task<IActionResult> GetMyGrades(int activityId)
+        {
+            var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+            var result = await _activityService.GetMyGrades(activityId, userId);
+
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok(result.Data);
+        }
+
+        [Authorize(Roles = UserTypes.Professor + ", " + UserTypes.FacultyAdmin)]
+        [HttpGet("GetAllGrades/{activityId}")]
+        public async Task<IActionResult> GetAllGrades(int activityId)
+        {
+            var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+            var result = await _activityService.GetAllGrades(activityId, userId);
+
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok(result.Data);
+        }
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/GradeDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/GradeDto.cs
new file mode 100644
index 0000000..4876ec9
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/GradeDto.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json.Serialization;
+
+namespace Tsi1.BusinessLayer.Dtos
+{
+    public class GradeDto
+    {
+        public int ActivityId { get; set; }
+
+        public int UserId { get; set; }
+
+        public decimal Value { get; set; }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
index 14da5d5..2b12422 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
@@ -67,5 +67,7 @@ namespace Tsi1.BusinessLayer.Helpers
         public const string InvalidTenant = "El usuario no pertenece a la facultad con id '{0}'";
 
         public const string ActivityDoesNotExist = "La actividad con id '{0}' no existe";
+
+        public const string GradeDoesNotExist = "La nota para la actividad con id '{0}' y usuario con id '{0}' no existe";
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
index 8116ab9..f0da4ed 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
@@ -57,6 +57,7 @@ namespace Tsi1.BusinessLayer.Helpers
             CreateMap<Activity, ActivityCreateDto>();
             CreateMap<Activity, ActivityModifyDto>();
             CreateMap<Activity, ActivityDto>();
+            CreateMap<Grade, GradeDto>();
 
             CreateMap<ForumCreateDto, Forum>();
             CreateMap<ForumPreviewDto, Forum>();
@@ -103,6 +104,7 @@ namespace Tsi1.BusinessLayer.Helpers
             CreateMap<ActivityCreateDto, Activity>();
             CreateMap<ActivityModifyDto, Activity>();
             CreateMap<ActivityDto, Activity>();
+            CreateMap<GradeDto, Grade>();
         }
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IActivityService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IActivityService.cs
index 4b0e88a..2dafbb7 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IActivityService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IActivityService.cs
@@ -16,5 +16,9 @@ namespace Tsi1.BusinessLayer.Interfaces
         Task<ServiceResult<int>> Delete(int activityId, int userId);
 
         Task<ServiceResult<int>> ActivityValidation(int activityId);
+        Task<ServiceResult<GradeDto>> AddOrModifyGrade(GradeDto gradeDto, int userId);
+        Task<ServiceResult<int>> DeleteGrade(int myUserId, int activityId, int userId);
+        Task<ServiceResult<List<GradeDto>>> GetMyGrades(int activityId, int userId);
+        Task<ServiceResult<List<GradeDto>>> GetAllGrades(int activityId, int userId);
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/ActivityService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/ActivityService.cs
index 9ab568e..706d49f 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Services/ActivityService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/ActivityService.cs
@@ -67,9 +67,9 @@ namespace Tsi1.BusinessLayer.Services
             return result;
         }
 
-        private async Task<ServiceResult<bool>> UserTypeValidation(int courseId, int userId)
+        private async Task<ServiceResult<User>> UserTypeValidation(int courseId, int userId)
         {
-            var result = new ServiceResult<bool>();
+            var result = new ServiceResult<User>();
 
             var user = await _context.Users.AsNoTracking()
                 .Include(x => x.UserType)
@@ -96,6 +96,21 @@ namespace Tsi1.BusinessLayer.Services
                 }
             }
 
+            if (user.UserType.Name == UserTypes.Student)
+            {
+                var isStudentCourse = await _context.StudentCourses
+                    .AsNoTracking()
+                    .AnyAsync(x => x.StudentId == user.StudentId && x.CourseId == courseId);
+
+                if (!isStudentCourse)
+                {
+                    result.HasError = true;
+                    result.AddMessage(string.Format(ErrorMessages.StudentCourseDoesNotExists, user.Id, courseId));
+
+                }
+            }
+
+            result.Data = user;
             return result;
         }
 
@@ -223,5 +238,209 @@ namespace Tsi1.BusinessLayer.Services
             result.Data = activity.Id;
             return result;
         }
+
+        public async Task<ServiceResult<GradeDto>> AddOrModifyGrade(GradeDto gradeDto, int userId)
+        {
+            var result = new ServiceResult<GradeDto>();
+
+            var activity = await _context.Activities.FirstOrDefaultAsync(x => x.Id == gradeDto.ActivityId);
+
+            if (activity == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, gradeDto.ActivityId));
+                return result;
+            }
+
+            var userTypeValidation = await this.UserTypeValidation(activity.CourseId, userId);
+
+            if (userTypeValidation.HasError)
+            {
+                result.HasError = true;
+                result.AddMessage(userTypeValidation.Message);
+                return result;
+            }
+
+            var userStudent = await _context.Users
+                .Include(x => x.Student)
+                .FirstOrDefaultAsync(x => x.Id == gradeDto.UserId);
+
+            if (userStudent == null || userStudent.Student == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.StudentDoesNotExist, gradeDto.UserId));
+                return result;
+            }
+
+            var userStudentValidation = await this.UserTypeValidation(activity.CourseId, userStudent.Id);
+
+            if (userStudentValidation.HasError)
+            {
+                result.HasError = true;
+                result.AddMessage(userStudentValidation.Message);
+                return result;
+            }
+
+            var grade = await _context.Grades
+                .FirstOrDefaultAsync(x => x.ActivityId == gradeDto.ActivityId && x.StudentId == userStudent.StudentId);
+
+            if (grade == null)
+            {
+                grade = new Grade
+                {
+                    ActivityId = gradeDto.ActivityId,
+                    StudentId = (int)userStudent.StudentId
+                };
+
+                _context.Grades.Add(grade);
+            }
+            
+            grade.Value = gradeDto.Value;
+
+            
+            await _context.SaveChangesAsync();
+
+            result.Data = gradeDto;
+            return result;
+        }
+
+        public async Task<ServiceResult<int>> DeleteGrade(int myUserId, int activityId, int userId)
+        {
+            var result = new ServiceResult<int>();
+
+            var userStudent = await _context.Users
+                .AsNoTracking()
+                .Include(x => x.Student)
+                .FirstOrDefaultAsync(x => x.Id == userId);
+
+            if (userStudent == null || userStudent.Student == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.StudentDoesNotExist, userStudent.Id));
+                return result;
+            }
+
+            var grade = await _context.Grades
+                .FirstOrDefaultAsync(x => x.ActivityId == activityId && x.StudentId == userStudent.StudentId);
+
+            if (grade == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.GradeDoesNotExist, activityId, userId));
+                return result;
+            }
+
+            var activity = await _context.Activities.FirstOrDefaultAsync(x => x.Id == activityId);
+
+            if (activity == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
+                return result;
+            }
+
+            var userTypeValidation = await this.UserTypeValidation(activity.CourseId, userId);
+
+            if (userTypeValidation.HasError)
+            {
+                result.HasError = true;
+                result.AddMessage(userTypeValidation.Message);
+                return result;
+            }
+
+            _context.Grades.Remove(grade);
+            await _context.SaveChangesAsync();
+
+            return result;
+        }
+
+        public async Task<ServiceResult<List<GradeDto>>> GetMyGrades(int activityId, int userId)
+        {
+            var result = new ServiceResult<List<GradeDto>>();
+
+            var activity = await _context.Activities.AsNoTracking().FirstOrDefaultAsync(x => x.Id == activityId);
+
+            if (activity == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
+                return result;
+            }
+
+            var userTypeValidation = await this.UserTypeValidation(activity.CourseId, userId);
+
+            if (userTypeValidation.HasError)
+            {
+                result.HasError = true;
+                result.AddMessage(userTypeValidation.Message);
+                return result;
+            }
+
+            var user = userTypeValidation.Data;
+
+            var grades = await _context.Grades
+                .AsNoTracking()
+                .Where(x => x.ActivityId == activityId && x.StudentId == user.StudentId)
+                .ToListAsync();
+
+            result.Data = _mapper.Map<List<GradeDto>>(grades);
+            return result;
+        }
+
+        public async Task<ServiceResult<List<GradeDto>>> GetAllGrades(int activityId, int userId)
+        {
+            var result = new ServiceResult<List<GradeDto>>();
+
+            var activity = await _context.Activities.AsNoTracking().FirstOrDefaultAsync(x => x.Id == activityId);
+
+            if (activity == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
+                return result;
+            }
+
+            var userTypeValidation = await this.UserTypeValidation(activity.CourseId, userId);
+
+            if (userTypeValidation.HasError)
+            {
+                result.HasError = true;
+                result.AddMessage(userTypeValidation.Message);
+                return result;
+            }
+
+            var user = userTypeValidation.Data;
+
+            var grades = await _context.Grades
+                .AsNoTracking()
+                .Where(x => x.ActivityId == activityId)
+                .ToListAsync();
+
+            var studentIds = grades.Select(x => x.StudentId).ToList();
+
+            var users = await _context.Users
+                .AsNoTracking()
+                .Where(x => studentIds.Contains((int)x.StudentId))
+                .ToListAsync();
+
+            var gradeDtos = new List<GradeDto>();
+
+            foreach (var grade in grades)
+            {
+                var userStudent = users.FirstOrDefault(x => x.StudentId == grade.StudentId);
+
+                var gradeDto = new GradeDto
+                {
+                    ActivityId = grade.ActivityId,
+                    UserId = userStudent.Id,
+                    Value = grade.Value
+                };
+
+                gradeDtos.Add(gradeDto);
+            }
+
+            result.Data = gradeDtos;
+            return result;
+        }
     }
 }
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Activity.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Activity.cs
index 4a0ad03..ca3a040 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Entities/Activity.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Activity.cs
@@ -9,6 +9,7 @@ namespace Tsi1.DataLayer.Entities
         public Activity()
         {
             Attendances = new HashSet<Attendance>();
+            Grades = new HashSet<Grade>();
         }
 
         public int Id { get; set; }
@@ -22,5 +23,7 @@ namespace Tsi1.DataLayer.Entities
         public Course Course { get; set; }
 
         public ICollection<Attendance> Attendances { get; set; }
+
+        public ICollection<Grade> Grades { get; set; }
     }
 }
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Grade.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Grade.cs
new file mode 100644
index 0000000..c5c05e7
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Grade.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.DataLayer.Entities
+{
+    public class Grade
+    {
+        public int ActivityId { get; set; }
+
+        public int StudentId { get; set; }
+
+        public decimal Value { get; set; }
+
+        public DateTime Date { get; set; }
+
+        public Student Student { get; set; }
+
+        public Activity Activity { get; set; }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs
index 2688e37..866c516 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs
@@ -9,6 +9,7 @@ namespace Tsi1.DataLayer.Entities
         public Student()
         {
             StudentCourses = new HashSet<StudentCourse>();
+            Grades = new HashSet<Grade>();
         }
 
         public int Id { get; set; }
@@ -22,5 +23,7 @@ namespace Tsi1.DataLayer.Entities
         public Tenant Tenant { get; set; }
         public User User { get; set; }
         public ICollection<StudentCourse> StudentCourses { get; set; }
+
+        public ICollection<Grade> Grades { get; set; }
     }
 }
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/GradeConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/GradeConfiguration.cs
new file mode 100644
index 0000000..d8596a9
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/GradeConfiguration.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.DataLayer.EntityConfiguration
+{
+    public class GradeConfiguration : IEntityTypeConfiguration<Grade>
+    {
+        public void Configure(EntityTypeBuilder<Grade> builder)
+        {
+            builder.HasKey(x => new { x.ActivityId, x.StudentId});
+
+            builder.Property(x => x.Value)
+                .IsRequired()
+                .HasColumnType("NUMERIC(5,2)");
+
+            builder.HasOne(x => x.Activity)
+                .WithMany(x => x.Grades)
+                .HasForeignKey(x => x.ActivityId);
+
+            builder.HasOne(x => x.Student)
+                .WithMany(x => x.Grades)
+                .HasForeignKey(x => x.StudentId);
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.Designer.cs
new file mode 100644
index 0000000..5c64c37
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.Designer.cs
@@ -0,0 +1,941 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Tsi1.DataLayer;
+
+namespace Tsi1.DataLayer.Migrations
+{
+    [DbContext(typeof(Tsi1Context))]
+    [Migration("20201130004426_add-grade-entity")]
+    partial class addgradeentity
+    {
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
+                .HasAnnotation("ProductVersion", "3.1.4")
+                .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Activity", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.Property<bool>("IsVideoConference")
+                        .HasColumnType("boolean");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("Activities");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.AnswerOption", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(100)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name");
+
+                    b.ToTable("AnswerOptions");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Attendance", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("ActivityId")
+                        .HasColumnType("integer");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ActivityId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("Attendances");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Communication", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int?>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.Property<bool>("IsGlobal")
+                        .HasColumnType("boolean");
+
+                    b.Property<int?>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Text")
+                        .IsRequired()
+                        .HasColumnType("character varying(1000)");
+
+                    b.Property<DateTime>("ValidUntil")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CourseId");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("ValidUntil");
+
+                    b.ToTable("Communications");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.Property<string>("ConnectionId")
+                        .HasColumnType("text");
+
+                    b.Property<string>("GroupName")
+                        .HasColumnType("text");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ConnectionId");
+
+                    b.HasIndex("GroupName");
+
+                    b.ToTable("Connections");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<bool>("IsTemplate")
+                        .HasColumnType("boolean");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("Name", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Courses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<string>("Path")
+                        .IsRequired()
+                        .HasColumnType("character varying(1000)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Path")
+                        .IsUnique();
+
+                    b.ToTable("Files");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Forums");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+                {
+                    b.Property<int>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ForumId", "UserId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("ForumUsers");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Grade", b =>
+                {
+                    b.Property<int>("ActivityId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<decimal>("Value")
+                        .HasColumnType("NUMERIC(5,2)");
+
+                    b.HasKey("ActivityId", "StudentId");
+
+                    b.HasIndex("StudentId");
+
+                    b.ToTable("Grades");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
+                {
+                    b.Property<string>("Name")
+                        .HasColumnType("text");
+
+                    b.HasKey("Name");
+
+                    b.ToTable("Groups");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<int>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Title")
+                        .IsRequired()
+                        .HasColumnType("character varying(100)");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId");
+
+                    b.HasIndex("ForumId", "Title")
+                        .IsUnique();
+
+                    b.ToTable("Posts");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Content")
+                        .IsRequired()
+                        .HasColumnType("character varying(10485760)");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<int>("PostId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("PostId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("PostMessages");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("IdentityCard")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("IdentityCard", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Professors");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+                {
+                    b.Property<int>("ProfessorId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ProfessorId", "CourseId");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("ProfessorCourses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<int>("Order")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("Sections");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int?>("FileId")
+                        .HasColumnType("integer");
+
+                    b.Property<int?>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("Order")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("SectionId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("SectionItemTypeId")
+                        .HasColumnType("integer");
+
+                    b.Property<int?>("SurveyId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("FileId")
+                        .IsUnique();
+
+                    b.HasIndex("ForumId")
+                        .IsUnique();
+
+                    b.HasIndex("SectionId");
+
+                    b.HasIndex("SectionItemTypeId");
+
+                    b.HasIndex("SurveyId")
+                        .IsUnique();
+
+                    b.ToTable("SectionItems");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItemType", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(100)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SectionItemTypes");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("Age")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("IdentityCard")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("IdentityCard", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Students");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+                {
+                    b.Property<int>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("StudentId", "CourseId");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("StudentCourses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Survey", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<bool>("IsGlobal")
+                        .HasColumnType("boolean");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<int?>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name");
+
+                    b.HasIndex("TenantId");
+
+                    b.ToTable("Surveys");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SurveyAnswer", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("AnswerOptionId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("SurveyQuestionId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("SurveyResponseId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("AnswerOptionId");
+
+                    b.HasIndex("SurveyQuestionId");
+
+                    b.HasIndex("SurveyResponseId");
+
+                    b.ToTable("SurveyAnswers");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SurveyQuestion", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Question")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<int>("SurveyId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("SurveyId");
+
+                    b.ToTable("SurveyQuestions");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SurveyResponse", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("SurveyId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("SurveyId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("SurveyResponses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.ToTable("Tenants");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("FirstName")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("LastName")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("Password")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<int?>("ProfessorId")
+                        .HasColumnType("integer");
+
+                    b.Property<int?>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserTypeId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Username")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ProfessorId")
+                        .IsUnique();
+
+                    b.HasIndex("StudentId")
+                        .IsUnique();
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("UserTypeId");
+
+                    b.HasIndex("Username", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Users");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.ToTable("UserTypes");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Activity", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("Activities")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Attendance", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Activity", "Activity")
+                        .WithMany("Attendances")
+                        .HasForeignKey("ActivityId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("Attendances")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Communication", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("Communications")
+                        .HasForeignKey("CourseId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Communications")
+                        .HasForeignKey("TenantId");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
+                        .WithMany("Connections")
+                        .HasForeignKey("GroupName");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Courses")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithMany("ForumUsers")
+                        .HasForeignKey("ForumId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("ForumUsers")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Grade", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Activity", "Activity")
+                        .WithMany("Grades")
+                        .HasForeignKey("ActivityId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithMany("Grades")
+                        .HasForeignKey("StudentId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithMany("Posts")
+                        .HasForeignKey("ForumId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("Posts")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Post", "Post")
+                        .WithMany("PostMessages")
+                        .HasForeignKey("PostId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("PostMessages")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Professors")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("ProfessorCourses")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+                        .WithMany("ProfessorCourses")
+                        .HasForeignKey("ProfessorId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("Sections")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.File", "File")
+                        .WithOne("SectionItem")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "FileId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithOne("SectionItem")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "ForumId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Section", "Section")
+                        .WithMany("SectionItems")
+                        .HasForeignKey("SectionId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.SectionItemType", "SectionItemType")
+                        .WithMany("SectionItems")
+                        .HasForeignKey("SectionItemTypeId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Survey", "Survey")
+                        .WithOne("SectionItem")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "SurveyId");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Students")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("StudentCourses")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithMany("StudentCourses")
+                        .HasForeignKey("StudentId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Survey", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Surveys")
+                        .HasForeignKey("TenantId");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SurveyAnswer", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.AnswerOption", "AnswerOption")
+                        .WithMany("SurveyAnswers")
+                        .HasForeignKey("AnswerOptionId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.SurveyQuestion", "SurveyQuestion")
+                        .WithMany("SurveyAnswers")
+                        .HasForeignKey("SurveyQuestionId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.SurveyResponse", "SurveyResponse")
+                        .WithMany("SurveyAnswers")
+                        .HasForeignKey("SurveyResponseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SurveyQuestion", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Survey", "Survey")
+                        .WithMany("SurveyQuestions")
+                        .HasForeignKey("SurveyId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SurveyResponse", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Survey", "Survey")
+                        .WithMany("SurveyResponses")
+                        .HasForeignKey("SurveyId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("SurveyResponses")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+                        .WithOne("User")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithOne("User")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Users")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType")
+                        .WithMany()
+                        .HasForeignKey("UserTypeId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.cs
new file mode 100644
index 0000000..8dd5eb3
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201130004426_add-grade-entity.cs
@@ -0,0 +1,48 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Tsi1.DataLayer.Migrations
+{
+    public partial class addgradeentity : Migration
+    {
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "Grades",
+                columns: table => new
+                {
+                    ActivityId = table.Column<int>(nullable: false),
+                    StudentId = table.Column<int>(nullable: false),
+                    Value = table.Column<decimal>(type: "NUMERIC(5,2)", nullable: false),
+                    Date = table.Column<DateTime>(nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_Grades", x => new { x.ActivityId, x.StudentId });
+                    table.ForeignKey(
+                        name: "FK_Grades_Activities_ActivityId",
+                        column: x => x.ActivityId,
+                        principalTable: "Activities",
+                        principalColumn: "Id",
+                        onDelete: ReferentialAction.Cascade);
+                    table.ForeignKey(
+                        name: "FK_Grades_Students_StudentId",
+                        column: x => x.StudentId,
+                        principalTable: "Students",
+                        principalColumn: "Id",
+                        onDelete: ReferentialAction.Cascade);
+                });
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Grades_StudentId",
+                table: "Grades",
+                column: "StudentId");
+        }
+
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "Grades");
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
index 08b6205..c7ee2e5 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
@@ -219,6 +219,27 @@ namespace Tsi1.DataLayer.Migrations
                     b.ToTable("ForumUsers");
                 });
 
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Grade", b =>
+                {
+                    b.Property<int>("ActivityId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<decimal>("Value")
+                        .HasColumnType("NUMERIC(5,2)");
+
+                    b.HasKey("ActivityId", "StudentId");
+
+                    b.HasIndex("StudentId");
+
+                    b.ToTable("Grades");
+                });
+
             modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
                 {
                     b.Property<string>("Name")
@@ -709,6 +730,21 @@ namespace Tsi1.DataLayer.Migrations
                         .IsRequired();
                 });
 
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Grade", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Activity", "Activity")
+                        .WithMany("Grades")
+                        .HasForeignKey("ActivityId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithMany("Grades")
+                        .HasForeignKey("StudentId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
             modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
                 {
                     b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
diff --git a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
index bda60bb..e99928e 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
@@ -36,6 +36,8 @@ namespace Tsi1.DataLayer
         public DbSet<Activity> Activities { get; set; }
         public DbSet<Attendance> Attendances { get; set; }
 
+        public DbSet<Grade> Grades { get; set; }
+
 
 
         public Tsi1Context(DbContextOptions options) : base(options) { }
@@ -68,6 +70,7 @@ namespace Tsi1.DataLayer
             modelBuilder.ApplyConfiguration(new CommunicationConfiguration());
             modelBuilder.ApplyConfiguration(new ActivityConfiguration());
             modelBuilder.ApplyConfiguration(new AttendanceConfiguration());
+            modelBuilder.ApplyConfiguration(new GradeConfiguration());
         }
     }
 }
-- 
GitLab