Skip to content
Snippets Groups Projects
Commit 8a722798 authored by Enzo Santangelo Dodera's avatar Enzo Santangelo Dodera
Browse files

Merge branch 'feature/activities' into 'develop'

Feature/activities

See merge request !32
parents 399f38e7 a87c53fd
No related branches found
No related tags found
1 merge request!32Feature/activities
Pipeline #10475 passed
Showing
with 584 additions and 12 deletions
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Dtos;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.BusinessLayer.Interfaces;
namespace Tsi1.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ActivityController : ControllerBase
{
private readonly IActivityService _activityService;
public ActivityController(IActivityService activityService)
{
_activityService = activityService;
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.Professor + ", " + UserTypes.Student)]
[HttpGet("GetAll/{courseId}")]
public async Task<IActionResult> GetAll(int courseId)
{
var result = await _activityService.GetAll(courseId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.Professor)]
[HttpPost("Create")]
public async Task<IActionResult> Create(ActivityCreateDto newActivity)
{
var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _activityService.Create(newActivity, userId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.Professor)]
[HttpPut("Modify/{activityId}")]
public async Task<IActionResult> Modify(int activityId, ActivityModifyDto activityDto)
{
var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _activityService.Modify(activityId, activityDto, userId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.Professor)]
[HttpDelete("Delete/{activityId}")]
public async Task<IActionResult> Delete(int activityId)
{
var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _activityService.Delete(activityId, userId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok();
}
}
}
......@@ -106,7 +106,7 @@ namespace Tsi1.Api.Controllers
[HttpGet("AllCourseStudentQuantity")]
public async Task<IActionResult> AllCourseStudentQuantity()
{
var result = await _tenantService.CourseStudentQuantity();
var result = await _tenantService.AllCourseStudentQuantity();
if (result.HasError)
{
......@@ -145,5 +145,21 @@ namespace Tsi1.Api.Controllers
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.FacultyAdmin)]
[HttpGet("CourseAttendance")]
public async Task<IActionResult> CourseAttendance()
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
var result = await _tenantService.CourseAttendance(tenantId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data);
}
}
}
......@@ -5,12 +5,20 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.BusinessLayer.Interfaces;
namespace Tsi1.Api.SignalR
{
[Authorize]
public class VideoHub : Hub
{
private readonly IActivityService _activityService;
public VideoHub(IActivityService activityService)
{
_activityService = activityService;
}
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, Context.ConnectionId);
......@@ -19,18 +27,34 @@ namespace Tsi1.Api.SignalR
}
[Authorize(Roles = UserTypes.Professor)]
public async Task CreateRoom(string roomName)
public async Task CreateRoom(int activityId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
var result = await _activityService.ActivityValidation(activityId);
if (result.HasError)
{
throw new HubException(result.Message);
}
await Clients.Caller.SendAsync("CreateRoom", roomName);
await Groups.AddToGroupAsync(Context.ConnectionId, activityId.ToString());
await Clients.Caller.SendAsync("CreateRoom", activityId);
}
public async Task Join(string roomName, string offer)
public async Task Join(int activityId, string offer)
{
await Clients.Group(roomName).SendAsync("Join", Context.ConnectionId, offer);
var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _activityService.AttendVirtualClass(activityId, userId);
if (result.HasError)
{
throw new HubException(result.Message);
}
await Clients.Group(activityId.ToString()).SendAsync("Join", Context.ConnectionId, offer);
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
await Groups.AddToGroupAsync(Context.ConnectionId, activityId.ToString());
}
public async Task Leave(string roomName)
......
......@@ -95,6 +95,7 @@ namespace Tsi1.Api
services.AddScoped<ISurveyService, SurveyService>();
services.AddScoped<ICommunicationService, CommunicationService>();
services.AddScoped<IChatService, ChatService>();
services.AddScoped<IActivityService, ActivityService>();
services.AddSingleton<PresenceTracker>();
......
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class ActivityCreateDto
{
public string Name { get; set; }
public bool IsVideoConference { get; set; }
public int CourseId { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class ActivityDto
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsVideoConference { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class ActivityModifyDto
{
public string Name { get; set; }
public bool IsVideoConference { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class CourseAttendanceDto
{
public int Id { get; set; }
public string Name { get; set; }
public List<UserAttendanceDto> Users { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class UserAttendanceDto
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int Quantity { get; set; }
}
}
......@@ -12,7 +12,7 @@ namespace Tsi1.BusinessLayer.Helpers
public const string StudentDoesNotExist = "El estudiante con Id de usuario: '{0}' no existe";
public const string StudentCourseDoesNotExists = "El estudiante '{0}' no se encuentra matriculado en el curso '{1}'";
public const string StudentCourseAlreadyExists = "El estudiante '{0}' ya se encuentra matriculado en el curso '{1}'";
public const string ProffesorDoesNotExist = "El profesor con Id de usuario: '{0}' no existe";
public const string ProfessorDoesNotExist = "El profesor con Id de usuario: '{0}' no existe";
public const string ProfessorCourseAlreadyExists = "El profesor '{0}' ya es docente del curso '{1}'";
public const string ProfessorCourseDoesNotExists = "El profesor '{0}' no es docente del curso '{1}'";
public const string InvalidUsername = "El nombre de usuario debe ser de la forma: 'usuario@facultad'";
......@@ -64,5 +64,7 @@ namespace Tsi1.BusinessLayer.Helpers
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}'";
public const string ActivityDoesNotExist = "La actividad con id '{0}' no existe";
}
}
......@@ -54,6 +54,9 @@ namespace Tsi1.BusinessLayer.Helpers
CreateMap<AnswerOption, AnswerOptionDetailDto>();
CreateMap<Tenant, RegistrationDto>();
CreateMap<Course, RegistrationDto>();
CreateMap<Activity, ActivityCreateDto>();
CreateMap<Activity, ActivityModifyDto>();
CreateMap<Activity, ActivityDto>();
CreateMap<ForumCreateDto, Forum>();
CreateMap<ForumPreviewDto, Forum>();
......@@ -97,6 +100,9 @@ namespace Tsi1.BusinessLayer.Helpers
CreateMap<AnswerOptionDetailDto, AnswerOption>();
CreateMap<RegistrationDto, Tenant>();
CreateMap<RegistrationDto, Course>();
CreateMap<ActivityCreateDto, Activity>();
CreateMap<ActivityModifyDto, Activity>();
CreateMap<ActivityDto, Activity>();
}
}
}
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 IActivityService
{
Task<ServiceResult<bool>> AttendVirtualClass(int activityId, int userId);
Task<ServiceResult<List<ActivityDto>>> GetAll(int courseId);
Task<ServiceResult<int>> Create(ActivityCreateDto newActivity, int userId);
Task<ServiceResult<int>> Modify(int activityId, ActivityModifyDto activityDto, int userId);
Task<ServiceResult<int>> Delete(int activityId, int userId);
Task<ServiceResult<int>> ActivityValidation(int activityId);
}
}
......@@ -23,10 +23,11 @@ namespace Tsi1.BusinessLayer.Interfaces
Task<ServiceResult<List<TenantPreviewDto>>> FacultyList();
Task<ServiceResult<List<TenantCourseDto>>> CourseStudentQuantity();
Task<ServiceResult<List<TenantCourseDto>>> AllCourseStudentQuantity();
Task<ServiceResult<List<RegistrationDto>>> FacultyRegistrations();
Task<ServiceResult<List<RegistrationDto>>> CourseStudentQuantity(int tenantId);
Task<ServiceResult<List<CourseAttendanceDto>>> CourseAttendance(int tenantId);
}
}
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 ActivityService : IActivityService
{
private readonly Tsi1Context _context;
private readonly IMapper _mapper;
public ActivityService(Tsi1Context context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<ServiceResult<bool>> AttendVirtualClass(int activityId, int userId)
{
var result = new ServiceResult<bool>();
var activity = await _context.Activities.FirstOrDefaultAsync(x => x.Id == activityId);
if (activity == null)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
}
var attendance = await _context.Attendances
.FirstOrDefaultAsync(x => x.ActivityId == activityId && x.UserId == userId);
if (attendance == null)
{
attendance = new Attendance
{
Activity = activity,
UserId = userId,
Date = DateTime.Now,
};
}
return result;
}
public async Task<ServiceResult<int>> ActivityValidation(int activityId)
{
var result = new ServiceResult<int>();
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;
}
private async Task<ServiceResult<bool>> UserTypeValidation(int courseId, int userId)
{
var result = new ServiceResult<bool>();
var user = await _context.Users.AsNoTracking()
.Include(x => x.UserType)
.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.UserDoesNotExist, userId));
return result;
}
if (user.UserType.Name == UserTypes.Professor)
{
var isProfessorCourse = await _context.ProfessorCourses
.AsNoTracking()
.AnyAsync(x => x.ProfessorId == user.ProfessorId && x.CourseId == courseId);
if (!isProfessorCourse)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.ProfessorCourseDoesNotExists, user.Id, courseId));
}
}
return result;
}
private async Task<ServiceResult<bool>> CourseValidation(int courseId)
{
var result = new ServiceResult<bool>();
var course = await _context.Courses.AsNoTracking().FirstOrDefaultAsync(x => x.Id == courseId);
if (course == null)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.CourseDoesNotExist, courseId));
}
return result;
}
public async Task<ServiceResult<List<ActivityDto>>> GetAll(int courseId)
{
var result = new ServiceResult<List<ActivityDto>>();
var validation = await this.CourseValidation(courseId);
if (validation.HasError)
{
result.HasError = true;
result.AddMessage(validation.Message);
return result;
}
var activities = await _context.Activities.AsNoTracking().Where(x => x.CourseId == courseId).ToListAsync();
result.Data = _mapper.Map<List<ActivityDto>>(activities);
return result;
}
public async Task<ServiceResult<int>> Create(ActivityCreateDto newActivity, int userId)
{
var result = new ServiceResult<int>();
var courseValidation = await this.CourseValidation(newActivity.CourseId);
if (courseValidation.HasError)
{
result.HasError = true;
result.AddMessage(courseValidation.Message);
return result;
}
var userTypeValidation = await this.UserTypeValidation(newActivity.CourseId, userId);
if (userTypeValidation.HasError)
{
result.HasError = true;
result.AddMessage(userTypeValidation.Message);
return result;
}
var activity = _mapper.Map<Activity>(newActivity);
_context.Activities.Add(activity);
await _context.SaveChangesAsync();
result.Data = activity.Id;
return result;
}
public async Task<ServiceResult<int>> Modify(int activityId, ActivityModifyDto activityDto, int userId)
{
var result = new ServiceResult<int>();
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;
}
_mapper.Map(activityDto, activity);
await _context.SaveChangesAsync();
result.Data = activity.Id;
return result;
}
public async Task<ServiceResult<int>> Delete(int activityId, int userId)
{
var result = new ServiceResult<int>();
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.Activities.Remove(activity);
await _context.SaveChangesAsync();
result.Data = activity.Id;
return result;
}
}
}
......@@ -158,7 +158,7 @@ namespace Tsi1.BusinessLayer.Services
return result;
}
public async Task<ServiceResult<List<TenantCourseDto>>> CourseStudentQuantity()
public async Task<ServiceResult<List<TenantCourseDto>>> AllCourseStudentQuantity()
{
var result = new ServiceResult<List<TenantCourseDto>>();
......@@ -238,5 +238,69 @@ namespace Tsi1.BusinessLayer.Services
return result;
}
public async Task<ServiceResult<List<CourseAttendanceDto>>> CourseAttendance(int tenantId)
{
var result = new ServiceResult<List<CourseAttendanceDto>>();
var tenant = await _context.Tenants.FirstOrDefaultAsync(x => x.Id == tenantId && x.Name != TenantAdmin.Name);
if (tenant == null)
{
result.HasError = true;
result.AddMessage(string.Format(ErrorMessages.TenantDoesNotExist, tenantId));
return result;
}
var courses = await _context.Courses
.AsNoTracking()
.Include(x => x.StudentCourses)
.Where(x => x.TenantId == tenantId && !x.IsTemplate)
.ToListAsync();
var attendances = await _context.Attendances
.AsNoTracking()
.Include(x => x.Activity)
.Where(x => x.User.TenantId == tenantId)
.ToListAsync();
var courseAttendanceDtos = new List<CourseAttendanceDto>();
foreach (var course in courses)
{
var courseAttendanceDto = new CourseAttendanceDto
{
Id = course.Id,
Name = course.Name,
Users = new List<UserAttendanceDto>(),
};
var studentIds = course.StudentCourses.Select(x => x.StudentId).ToList();
var users = await _context.Users
.AsNoTracking()
.Where(x => x.TenantId == tenantId && studentIds.Contains((int)x.StudentId))
.ToListAsync();
foreach (var user in users)
{
var attendanceQuantity = attendances.Where(x => x.UserId == user.Id && x.Activity.CourseId == course.Id).Count();
var userAttendanceDto = new UserAttendanceDto
{
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
Quantity = attendanceQuantity
};
courseAttendanceDto.Users.Add(userAttendanceDto);
}
courseAttendanceDtos.Add(courseAttendanceDto);
}
result.Data = courseAttendanceDtos;
return result;
}
}
}
......@@ -178,7 +178,7 @@ namespace Tsi1.BusinessLayer.Services
else if(userType == UserTypes.Professor && user.Professor == null)
{
result.HasError = true;
result.Message = string.Format(ErrorMessages.ProffesorDoesNotExist, userId);
result.Message = string.Format(ErrorMessages.ProfessorDoesNotExist, userId);
return result;
}
......@@ -219,7 +219,7 @@ namespace Tsi1.BusinessLayer.Services
else if (userType == UserTypes.Professor && user.Professor == null)
{
result.HasError = true;
result.Message = string.Format(ErrorMessages.ProffesorDoesNotExist, username);
result.Message = string.Format(ErrorMessages.ProfessorDoesNotExist, username);
return result;
}
......
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.DataLayer.Entities
{
public class Activity
{
public Activity()
{
Attendances = new HashSet<Attendance>();
}
public int Id { get; set; }
public string Name { get; set; }
public bool IsVideoConference { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
public ICollection<Attendance> Attendances { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.DataLayer.Entities
{
public class Attendance
{
public int Id { get; set; }
public DateTime Date { get; set; }
public int UserId { get; set; }
public int ActivityId { get; set; }
public User User { get; set; }
public Activity Activity { get; set; }
}
}
......@@ -12,6 +12,7 @@ namespace Tsi1.DataLayer.Entities
ProfessorCourses = new HashSet<ProfessorCourse>();
Sections = new HashSet<Section>();
Communications = new HashSet<Communication>();
Activities = new HashSet<Activity>();
}
public int Id { get; set; }
......@@ -25,5 +26,7 @@ namespace Tsi1.DataLayer.Entities
public ICollection<Section> Sections { get; set; }
public ICollection<Communication> Communications { get; set; }
public ICollection<Activity> Activities { get; set; }
}
}
......@@ -12,6 +12,7 @@ namespace Tsi1.DataLayer.Entities
PostMessages = new HashSet<PostMessage>();
ForumUsers = new HashSet<ForumUser>();
SurveyResponses = new HashSet<SurveyResponse>();
Attendances = new HashSet<Attendance>();
}
public int Id { get; set; }
......@@ -43,5 +44,6 @@ namespace Tsi1.DataLayer.Entities
public ICollection<PostMessage> PostMessages { get; set; }
public ICollection<ForumUser> ForumUsers { get; set; }
public ICollection<SurveyResponse> SurveyResponses { get; set; }
public ICollection<Attendance> Attendances { get; set; }
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment