Skip to content
Snippets Groups Projects
Commit ea7e6ea1 authored by Lucca Santangelo Dodera's avatar Lucca Santangelo Dodera
Browse files

Merge branch 'feature/Communications' into 'develop'

Feature/communications

See merge request !25
parents 03fb7bd5 5c738726
No related branches found
No related tags found
2 merge requests!26Develop,!25Feature/communications
Pipeline #10365 passed
Showing
with 2254 additions and 0 deletions
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Tsi1.BusinessLayer.Dtos;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.BusinessLayer.Interfaces;
namespace Tsi1.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CommunicationController : ControllerBase
{
private readonly ICommunicationService _communicationService;
public CommunicationController(ICommunicationService communicationService)
{
_communicationService = communicationService;
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.UdelarAdmin)]
[HttpPost("CreateCourseCommunication/{courseId}")]
public async Task<IActionResult> CreateCourseCommunication(CommunicationCreateDto newCommunication, int courseId)
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
var validationResult = await _communicationService.TenantValidation(tenantId, courseId);
if (validationResult.HasError)
{
return BadRequest(validationResult.Message);
}
var result = await _communicationService.Create(newCommunication, courseId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.UdelarAdmin)]
[HttpPost("CreateTenantCommunication")]
public async Task<IActionResult> CreateTenantCommunication(CommunicationCreateDto newCommunication, int tenantId)
{
var userType = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Role).Value;
if (userType == UserTypes.FacultyAdmin)
{
tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
}
newCommunication.IsGlobal = true;
var result = await _communicationService.Create(newCommunication, tenantId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.Student)]
[HttpGet("GetMyCommunications")]
public async Task<IActionResult> GetMyCommunications()
{
var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _communicationService.GetMyCommunications(userId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.UdelarAdmin)]
[HttpDelete("Delete/{communicationId}")]
public async Task<IActionResult> Delete(int communicationId)
{
var result = await _communicationService.Delete(communicationId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok();
}
}
}
...@@ -92,6 +92,7 @@ namespace Tsi1.Api ...@@ -92,6 +92,7 @@ namespace Tsi1.Api
services.AddScoped<ISectionItemTypeService, SectionItemTypeService>(); services.AddScoped<ISectionItemTypeService, SectionItemTypeService>();
services.AddScoped<IDataLoad, DataLoad>(); services.AddScoped<IDataLoad, DataLoad>();
services.AddScoped<ISurveyService, SurveyService>(); services.AddScoped<ISurveyService, SurveyService>();
services.AddScoped<ICommunicationService, CommunicationService>();
services.AddScoped<IChatService, ChatService>(); services.AddScoped<IChatService, ChatService>();
services.AddSingleton<PresenceTracker>(); services.AddSingleton<PresenceTracker>();
......
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization;
namespace Tsi1.BusinessLayer.Dtos
{
public class CommunicationCreateDto
{
public string Text { get; set; }
[JsonIgnore]
public bool IsGlobal { get; set; }
public DateTime ValidUntil { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class CommunicationPreviewDto
{
public int Id { get; set; }
public string Text { get; set; }
}
}
...@@ -61,5 +61,8 @@ namespace Tsi1.BusinessLayer.Helpers ...@@ -61,5 +61,8 @@ namespace Tsi1.BusinessLayer.Helpers
public const string SurveyHasNoQuestions = "La encuesta no tiene preguntas"; public const string SurveyHasNoQuestions = "La encuesta no tiene preguntas";
public const string InvalidSurvey = "La encuesta no pertenece a la facultad con id '{0}'"; public const string InvalidSurvey = "La encuesta no pertenece a la facultad con id '{0}'";
public const string SurveyResponseAlreadyExist = "El usuario '{0}' ya completo la encuesta '{1}'"; public const string SurveyResponseAlreadyExist = "El usuario '{0}' ya completo la encuesta '{1}'";
public const string CommunicationDoesNotExist = "La comunicación con id '{0}' no existe";
public const string InvalidTenant = "El usuario no pertenece a la facultad con id '{0}'";
} }
} }
...@@ -49,6 +49,8 @@ namespace Tsi1.BusinessLayer.Helpers ...@@ -49,6 +49,8 @@ namespace Tsi1.BusinessLayer.Helpers
CreateMap<SurveyResponse, SurveyResponseDetailDto>(); CreateMap<SurveyResponse, SurveyResponseDetailDto>();
CreateMap<SurveyAnswer, SurveyAnswerDetailDto>(); CreateMap<SurveyAnswer, SurveyAnswerDetailDto>();
CreateMap<SurveyAnswer, SurveyAnswerCreateDto>(); CreateMap<SurveyAnswer, SurveyAnswerCreateDto>();
CreateMap<Communication, CommunicationCreateDto>();
CreateMap<Communication, CommunicationPreviewDto>();
CreateMap<AnswerOption, AnswerOptionDetailDto>(); CreateMap<AnswerOption, AnswerOptionDetailDto>();
CreateMap<ForumCreateDto, Forum>(); CreateMap<ForumCreateDto, Forum>();
...@@ -88,6 +90,8 @@ namespace Tsi1.BusinessLayer.Helpers ...@@ -88,6 +90,8 @@ namespace Tsi1.BusinessLayer.Helpers
CreateMap<SurveyResponseDetailDto, SurveyResponse>(); CreateMap<SurveyResponseDetailDto, SurveyResponse>();
CreateMap<SurveyAnswerDetailDto, SurveyAnswer>(); CreateMap<SurveyAnswerDetailDto, SurveyAnswer>();
CreateMap<SurveyAnswerCreateDto, SurveyAnswer>(); CreateMap<SurveyAnswerCreateDto, SurveyAnswer>();
CreateMap<CommunicationCreateDto, Communication>();
CreateMap<CommunicationPreviewDto, Communication>();
CreateMap<AnswerOptionDetailDto, AnswerOption>(); CreateMap<AnswerOptionDetailDto, AnswerOption>();
} }
} }
......
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Dtos;
using Tsi1.BusinessLayer.Helpers;
namespace Tsi1.BusinessLayer.Interfaces
{
public interface ICommunicationService
{
Task<ServiceResult<bool>> Create(CommunicationCreateDto newCommunication, int id);
Task<ServiceResult<bool>> Delete(int communicationId);
Task<ServiceResult<List<CommunicationPreviewDto>>> GetMyCommunications(int userId);
Task<ServiceResult<bool>> TenantValidation(int tenantId, int courseId);
}
}
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Dtos;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.BusinessLayer.Interfaces;
using Tsi1.DataLayer;
using Tsi1.DataLayer.Entities;
namespace Tsi1.BusinessLayer.Services
{
public class CommunicationService : ICommunicationService
{
private readonly Tsi1Context _context;
private readonly IMapper _mapper;
public CommunicationService(Tsi1Context context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<ServiceResult<bool>> Create(CommunicationCreateDto newCommunication, int id)
{
var result = new ServiceResult<bool>();
var isGlobal = newCommunication.IsGlobal;
var commmunication = _mapper.Map<Communication>(newCommunication);
var validationResult = await this.CreateValidation(id, isGlobal, commmunication);
if (validationResult.HasError)
{
return validationResult;
}
_context.Communications.Add(commmunication);
await _context.SaveChangesAsync();
return result;
}
public async Task<ServiceResult<bool>> Delete(int communicationId)
{
var result = new ServiceResult<bool>();
var commmunication = await _context.Communications.FirstOrDefaultAsync(x => x.Id == communicationId);
if (commmunication == null)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.CommunicationDoesNotExist, communicationId));
return result;
}
_context.Communications.Remove(commmunication);
await _context.SaveChangesAsync();
return result;
}
public async Task<ServiceResult<List<CommunicationPreviewDto>>> GetMyCommunications(int userId)
{
var result = new ServiceResult<List<CommunicationPreviewDto>>();
var user = await _context.Users.AsNoTracking().FirstOrDefaultAsync(x => x.Id == userId);
var courseIds = await _context.StudentCourses
.AsNoTracking()
.Where(x => x.StudentId == user.StudentId)
.Select(x => x.CourseId)
.ToListAsync();
var communications = await _context.Communications
.AsNoTracking()
.Include(x => x.Tenant)
.Include(x => x.Course)
.Where(x => (x.Tenant.Id == userId || courseIds.Contains(x.Course.Id))
&& x.ValidUntil >= DateTime.Now)
.ToListAsync();
result.Data = _mapper.Map<List<CommunicationPreviewDto>>(communications);
return result;
}
public async Task<ServiceResult<bool>> TenantValidation(int tenantId, int courseId)
{
var result = new ServiceResult<bool>();
var tenantAdmin = await _context.Tenants.AsNoTracking().FirstOrDefaultAsync(x => x.Name == TenantAdmin.Name);
var course = await _context.Courses.AsNoTracking().FirstOrDefaultAsync(x => x.Id == courseId);
if (tenantAdmin.Id != tenantId)
{
result.HasError = course.TenantId != tenantId;
result.AddMessage(string.Format(ErrorMessages.InvalidTenant, course.TenantId));
}
return result;
}
private async Task<ServiceResult<bool>> CreateValidation(int id, bool isGlobal, Communication commmunication)
{
var result = new ServiceResult<bool>();
if (isGlobal)
{
var tenant = await _context.Tenants.FirstOrDefaultAsync(x => x.Id == id && x.Name != TenantAdmin.Name);
if (tenant == null)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.TenantDoesNotExist, id));
return result;
}
commmunication.Tenant = tenant;
}
else
{
var course = await _context.Courses.FirstOrDefaultAsync(x => x.Id == id);
if (course == null)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.CourseDoesNotExist, id));
return result;
}
commmunication.Course = course;
}
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.DataLayer.Entities
{
public class Communication
{
public int Id { get; set; }
public string Text { get; set; }
public bool IsGlobal { get; set; }
public DateTime ValidUntil { get; set; }
public Course Course { get; set; }
public Tenant Tenant { get; set; }
}
}
...@@ -11,6 +11,7 @@ namespace Tsi1.DataLayer.Entities ...@@ -11,6 +11,7 @@ namespace Tsi1.DataLayer.Entities
StudentCourses = new HashSet<StudentCourse>(); StudentCourses = new HashSet<StudentCourse>();
ProfessorCourses = new HashSet<ProfessorCourse>(); ProfessorCourses = new HashSet<ProfessorCourse>();
Sections = new HashSet<Section>(); Sections = new HashSet<Section>();
Communications = new HashSet<Communication>();
} }
public int Id { get; set; } public int Id { get; set; }
...@@ -22,5 +23,7 @@ namespace Tsi1.DataLayer.Entities ...@@ -22,5 +23,7 @@ namespace Tsi1.DataLayer.Entities
public ICollection<StudentCourse> StudentCourses { get; set; } public ICollection<StudentCourse> StudentCourses { get; set; }
public ICollection<ProfessorCourse> ProfessorCourses { get; set; } public ICollection<ProfessorCourse> ProfessorCourses { get; set; }
public ICollection<Section> Sections { get; set; } public ICollection<Section> Sections { get; set; }
public ICollection<Communication> Communications { get; set; }
} }
} }
...@@ -13,6 +13,7 @@ namespace Tsi1.DataLayer.Entities ...@@ -13,6 +13,7 @@ namespace Tsi1.DataLayer.Entities
Students = new HashSet<Student>(); Students = new HashSet<Student>();
Users = new HashSet<User>(); Users = new HashSet<User>();
Surveys = new HashSet<Survey>(); Surveys = new HashSet<Survey>();
Communications = new HashSet<Communication>();
} }
public int Id { get; set; } public int Id { get; set; }
...@@ -24,5 +25,7 @@ namespace Tsi1.DataLayer.Entities ...@@ -24,5 +25,7 @@ namespace Tsi1.DataLayer.Entities
public ICollection<Student> Students { get; set; } public ICollection<Student> Students { get; set; }
public ICollection<User> Users { get; set; } public ICollection<User> Users { get; set; }
public ICollection<Survey> Surveys { get; set; } public ICollection<Survey> Surveys { get; set; }
public ICollection<Communication> Communications { get; set; }
} }
} }
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 CommunicationConfiguration : IEntityTypeConfiguration<Communication>
{
public void Configure(EntityTypeBuilder<Communication> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Text)
.IsRequired()
.HasColumnType("character varying(1000)");
builder.Property(x => x.ValidUntil)
.IsRequired();
builder.HasIndex(x => x.ValidUntil);
}
}
}
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Tsi1.DataLayer.Migrations
{
public partial class addcommunicationentity : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Communication",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Text = table.Column<string>(nullable: true),
ValidUntil = table.Column<DateTime>(nullable: false),
CourseId = table.Column<int>(nullable: true),
TenantId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Communication", x => x.Id);
table.ForeignKey(
name: "FK_Communication_Courses_CourseId",
column: x => x.CourseId,
principalTable: "Courses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Communication_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_Communication_CourseId",
table: "Communication",
column: "CourseId");
migrationBuilder.CreateIndex(
name: "IX_Communication_TenantId",
table: "Communication",
column: "TenantId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Communication");
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tsi1.DataLayer.Migrations
{
public partial class addcolumntocommunication : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Communication_Courses_CourseId",
table: "Communication");
migrationBuilder.DropForeignKey(
name: "FK_Communication_Tenants_TenantId",
table: "Communication");
migrationBuilder.DropPrimaryKey(
name: "PK_Communication",
table: "Communication");
migrationBuilder.RenameTable(
name: "Communication",
newName: "Communications");
migrationBuilder.RenameIndex(
name: "IX_Communication_TenantId",
table: "Communications",
newName: "IX_Communications_TenantId");
migrationBuilder.RenameIndex(
name: "IX_Communication_CourseId",
table: "Communications",
newName: "IX_Communications_CourseId");
migrationBuilder.AlterColumn<string>(
name: "Text",
table: "Communications",
type: "character varying(1000)",
nullable: false,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AddColumn<bool>(
name: "IsGlobal",
table: "Communications",
nullable: false,
defaultValue: false);
migrationBuilder.AddPrimaryKey(
name: "PK_Communications",
table: "Communications",
column: "Id");
migrationBuilder.CreateIndex(
name: "IX_Communications_ValidUntil",
table: "Communications",
column: "ValidUntil");
migrationBuilder.AddForeignKey(
name: "FK_Communications_Courses_CourseId",
table: "Communications",
column: "CourseId",
principalTable: "Courses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Communications_Tenants_TenantId",
table: "Communications",
column: "TenantId",
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Communications_Courses_CourseId",
table: "Communications");
migrationBuilder.DropForeignKey(
name: "FK_Communications_Tenants_TenantId",
table: "Communications");
migrationBuilder.DropPrimaryKey(
name: "PK_Communications",
table: "Communications");
migrationBuilder.DropIndex(
name: "IX_Communications_ValidUntil",
table: "Communications");
migrationBuilder.DropColumn(
name: "IsGlobal",
table: "Communications");
migrationBuilder.RenameTable(
name: "Communications",
newName: "Communication");
migrationBuilder.RenameIndex(
name: "IX_Communications_TenantId",
table: "Communication",
newName: "IX_Communication_TenantId");
migrationBuilder.RenameIndex(
name: "IX_Communications_CourseId",
table: "Communication",
newName: "IX_Communication_CourseId");
migrationBuilder.AlterColumn<string>(
name: "Text",
table: "Communication",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(1000)");
migrationBuilder.AddPrimaryKey(
name: "PK_Communication",
table: "Communication",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Communication_Courses_CourseId",
table: "Communication",
column: "CourseId",
principalTable: "Courses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Communication_Tenants_TenantId",
table: "Communication",
column: "TenantId",
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}
...@@ -37,6 +37,40 @@ namespace Tsi1.DataLayer.Migrations ...@@ -37,6 +37,40 @@ namespace Tsi1.DataLayer.Migrations
b.ToTable("AnswerOptions"); b.ToTable("AnswerOptions");
}); });
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 => modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
{ {
b.Property<string>("ConnectionId") b.Property<string>("ConnectionId")
...@@ -560,6 +594,17 @@ namespace Tsi1.DataLayer.Migrations ...@@ -560,6 +594,17 @@ namespace Tsi1.DataLayer.Migrations
b.ToTable("UserTypes"); b.ToTable("UserTypes");
}); });
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 => modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
{ {
b.HasOne("Tsi1.DataLayer.Entities.Group", "Group") b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
......
...@@ -33,6 +33,8 @@ namespace Tsi1.DataLayer ...@@ -33,6 +33,8 @@ namespace Tsi1.DataLayer
public DbSet<SurveyResponse> SurveyResponses { get; set; } public DbSet<SurveyResponse> SurveyResponses { get; set; }
public DbSet<AnswerOption> AnswerOptions { get; set; } public DbSet<AnswerOption> AnswerOptions { get; set; }
public DbSet<Communication> Communications { get; set; }
public Tsi1Context(DbContextOptions options) : base(options) { } public Tsi1Context(DbContextOptions options) : base(options) { }
...@@ -62,6 +64,7 @@ namespace Tsi1.DataLayer ...@@ -62,6 +64,7 @@ namespace Tsi1.DataLayer
modelBuilder.ApplyConfiguration(new SurveyQuestionConfiguration()); modelBuilder.ApplyConfiguration(new SurveyQuestionConfiguration());
modelBuilder.ApplyConfiguration(new SurveyResponseConfiguration()); modelBuilder.ApplyConfiguration(new SurveyResponseConfiguration());
modelBuilder.ApplyConfiguration(new AnswerOptionConfiguration()); modelBuilder.ApplyConfiguration(new AnswerOptionConfiguration());
modelBuilder.ApplyConfiguration(new CommunicationConfiguration());
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment