From 9b71c25a372f084ab33a3057b746b292cc056d25 Mon Sep 17 00:00:00 2001 From: Taco Droogers <34547552+TDroogers@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:59:30 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8=20Set=20user=20function=20in=20cal?= =?UTF-8?q?ender=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Server/Controllers/ScheduleController.cs | 54 +++++++++++++------ src/Server/Controllers/UserController.cs | 6 +-- src/Server/Mappers/FunctionMapper.cs | 22 ++++++++ src/Server/Mappers/PreComMapper.cs | 1 + src/Server/Services/FunctionService.cs | 27 +++++++++- .../Services/Interfaces/IFunctionService.cs | 1 + .../Services/Interfaces/IScheduleService.cs | 2 +- .../Services/Interfaces/IUserService.cs | 4 +- src/Server/Services/ScheduleService.cs | 10 ++-- src/Server/Services/UserService.cs | 6 +-- 10 files changed, 102 insertions(+), 31 deletions(-) create mode 100644 src/Server/Mappers/FunctionMapper.cs diff --git a/src/Server/Controllers/ScheduleController.cs b/src/Server/Controllers/ScheduleController.cs index c3fadae4..fe7829f4 100644 --- a/src/Server/Controllers/ScheduleController.cs +++ b/src/Server/Controllers/ScheduleController.cs @@ -9,7 +9,9 @@ using Microsoft.Identity.Web.Resource; using System.Diagnostics; using System.Security.Claims; +using System.Text; using System.Text.Json; +using Drogecode.Knrm.Oefenrooster.Shared.Models.Function; namespace Drogecode.Knrm.Oefenrooster.Server.Controllers; @@ -29,6 +31,7 @@ public class ScheduleController : ControllerBase private readonly IUserSettingService _userSettingService; private readonly IDateTimeService _dateTimeService; private readonly IUserService _userService; + private readonly IFunctionService _functionService; public ScheduleController( RefreshHub refreshHub, @@ -39,7 +42,8 @@ public ScheduleController( ITrainingTypesService trainingTypesService, IUserSettingService userSettingService, IDateTimeService dateTimeService, - IUserService userService) + IUserService userService, + IFunctionService functionService) { _refreshHub = refreshHub; _logger = logger; @@ -50,6 +54,7 @@ public ScheduleController( _userSettingService = userSettingService; _dateTimeService = dateTimeService; _userService = userService; + _functionService = functionService; } [HttpGet] @@ -204,7 +209,6 @@ private async Task PatchTrainingCalenderUsers(Guid trainingId, Guid customerId, clt.ThrowIfCancellationRequested(); _graphService.InitializeGraph(); - var text = GetTrainingCalenderText(training.Training?.TrainingTypeName, training.Training?.Name); if (training.Training?.PlanUsers.Count > 0) { foreach (var user in training.Training.PlanUsers) @@ -212,22 +216,28 @@ private async Task PatchTrainingCalenderUsers(Guid trainingId, Guid customerId, await _refreshHub.SendMessage(user.UserId, ItemUpdated.FutureTrainings); if (!string.IsNullOrEmpty(user.CalendarEventId)) { + DrogeFunction? function = null; + if (user.PlannedFunctionId is not null && user.UserFunctionId is not null && user.UserFunctionId != user.PlannedFunctionId) + function = await _functionService.GetById(customerId, user.PlannedFunctionId.Value, clt); + var text = GetTrainingCalenderText(training.Training.TrainingTypeName, training.Training.Name, function?.Name); await _graphService.PatchCalender(user.UserId, user.CalendarEventId, text, training.Training.DateStart, training.Training.DateEnd, !training.Training.ShowTime); } } } } - private static string GetTrainingCalenderText(string? trainingTypeName, string? trainingName) + private static string GetTrainingCalenderText(string? trainingTypeName, string? trainingName, string? functionName) { - var text = string.Empty; + var text = new StringBuilder(); if (!string.IsNullOrEmpty(trainingTypeName)) - text += trainingTypeName; + text.Append(trainingTypeName); if (!string.IsNullOrEmpty(trainingTypeName) && !string.IsNullOrEmpty(trainingName)) - text += " - "; + text.Append(" - "); if (!string.IsNullOrEmpty(trainingName)) - text += trainingName; - return text; + text.Append(trainingName); + if (!string.IsNullOrEmpty(functionName)) + text.Append(" - ").Append(functionName); + return text.ToString(); } [HttpPost] @@ -301,21 +311,26 @@ public async Task> PatchAssignedUser([Fr await _auditService.Log(userId, AuditType.PatchAssignedUser, customerId, JsonSerializer.Serialize(new AuditAssignedUser { - UserId = body.User?.UserId, Assigned = body.User?.Assigned, Availability = result.Availability, SetBy = result.SetBy, + UserId = body.User?.UserId, Assigned = body.User?.Assigned, Availability = result?.Availability, SetBy = result?.SetBy, AuditReason = body.AuditReason ?? AuditReason.Assigned, // body.AuditReason was added in v0.3.81 VehicleId = body.AuditReason == AuditReason.ChangeVehicle ? body.User?.VehicleId : null, FunctionId = body.AuditReason == AuditReason.ChangedFunction ? body.User?.PlannedFunctionId : null }), body.TrainingId); await _userService.PatchLastOnline(userId, clt); - if (result.Success && body.User?.UserId is not null) + if (result?.Success == true && body.User?.UserId is not null) { clt = CancellationToken.None; - await ToOutlookCalendar(body.User.UserId, body.TrainingId, body.User.Assigned, body.Training, userId, customerId, result.AvailableId, result.CalendarEventId, clt); + DrogeFunction? function = null; + if (body.User.PlannedFunctionId is not null && body.User.UserFunctionId is not null && body.User.UserFunctionId != body.User.PlannedFunctionId) + function = await _functionService.GetById(customerId, body.User.PlannedFunctionId.Value, clt); + await ToOutlookCalendar(body.User.UserId, body.TrainingId, body.User.Assigned, body.Training, userId, customerId, result.AvailableId, result.CalendarEventId, function?.Name, clt); await _refreshHub.SendMessage(body.User.UserId, ItemUpdated.FutureTrainings); } - return result; + if (result is not null) + return result; + return BadRequest(); } catch (OperationCanceledException) { @@ -351,7 +366,11 @@ await _auditService.Log(userId, AuditType.PatchAssignedUser, customerId, if (result.Success && body.UserId is not null) { clt = CancellationToken.None; - await ToOutlookCalendar(body.UserId.Value, body.TrainingId, body.Assigned, body.Training, userId, customerId, result.AvailableId, result.CalendarEventId, clt); + var user = await _userService.GetUserById(body.UserId.Value, clt); + DrogeFunction? function = null; + if (body.Training?.PlannedFunctionId is not null && user?.UserFunctionId is not null && user.UserFunctionId != body.Training.PlannedFunctionId) + function = await _functionService.GetById(customerId, body.Training.PlannedFunctionId.Value, clt); + await ToOutlookCalendar(body.UserId.Value, body.TrainingId, body.Assigned, body.Training, userId, customerId, result.AvailableId, result.CalendarEventId, function?.Name, clt); await _refreshHub.SendMessage(body.UserId.Value, ItemUpdated.FutureTrainings); } @@ -495,7 +514,7 @@ public async Task> DeleteTraining(Guid id, CancellationToken } private async Task ToOutlookCalendar(Guid planUserId, Guid? trainingId, bool assigned, TrainingAdvance? training, Guid currentUserId, Guid customerId, Guid? availableId, string? calendarEventId, - CancellationToken clt) + string? functionName, CancellationToken clt) { try { @@ -508,7 +527,7 @@ private async Task ToOutlookCalendar(Guid planUserId, Guid? trainingId, bool ass if (training is null && trainingId is not null) training = (await _scheduleService.GetTrainingById(planUserId, customerId, trainingId.Value, clt)).Training; var type = await _trainingTypesService.GetById(training?.RoosterTrainingTypeId ?? Guid.Empty, customerId, clt); - var text = GetTrainingCalenderText(type.TrainingType?.Name, training?.Name); + var text = GetTrainingCalenderText(type.TrainingType?.Name, training?.Name, functionName); if (training is null) { _logger.LogWarning("Failed to set a training for trainingId {trainingId}", trainingId); @@ -532,8 +551,6 @@ private async Task ToOutlookCalendar(Guid planUserId, Guid? trainingId, bool ass else { await _graphService.PatchCalender(planUserId, calendarEventId, text, training.DateStart, training.DateEnd, !training.ShowTime); - await _auditService.Log(currentUserId, AuditType.PatchTraining, customerId, - $"Preventing duplicate event '{type.TrainingType?.Name}' on '{training.DateStart.ToString("o")}' : '{training.DateEnd.ToString("o")}'"); } } else if (!string.IsNullOrEmpty(calendarEventId)) @@ -545,6 +562,9 @@ await _auditService.Log(currentUserId, AuditType.PatchTraining, customerId, } catch (Exception ex) { +#if DEBUG + Debugger.Break(); +#endif _logger.LogError(ex, "Exception in ToOutlookCalendar {customerId} {planUserId} {trainingId} {assigned} {currentUserId} {availableId} {calendarEventId} and training is {trainingNullOrNot}", customerId, planUserId, trainingId, assigned, currentUserId, availableId, calendarEventId, training is null ? "null" : "not null"); } diff --git a/src/Server/Controllers/UserController.cs b/src/Server/Controllers/UserController.cs index f34857a5..523d17c6 100644 --- a/src/Server/Controllers/UserController.cs +++ b/src/Server/Controllers/UserController.cs @@ -71,7 +71,7 @@ public async Task> GetCurrentUser(Cancellatio var userName = User?.FindFirstValue("FullName") ?? throw new Exception("No userName found"); var userEmail = User?.FindFirstValue(ClaimTypes.Name) ?? throw new Exception("No userEmail found"); var customerId = new Guid(User?.FindFirstValue("http://schemas.microsoft.com/identity/claims/tenantid") ?? throw new DrogeCodeNullException("customerId not found")); - var result = await _userService.GetOrSetUserFromDb(userId, userName, userEmail, customerId, true); + var result = await _userService.GetOrSetUserById(userId, userName, userEmail, customerId, true); return new GetDrogeUserResponse { DrogeUser = result }; } @@ -88,7 +88,7 @@ public async Task> GetById(Guid id, CancellationTo { try { - var result = await _userService.GetUserFromDb(id); + var result = await _userService.GetUserById(id, clt); return new GetByIdResponse { User = result, Success = true }; } catch (Exception ex) @@ -190,7 +190,7 @@ public async Task> SyncAllUsers(CancellationT var index = existingUsers.FindIndex(x => x.Id == id); if (index != -1) existingUsers.RemoveAt(index); - var newUserResponse = await _userService.GetOrSetUserFromDb(id, user.DisplayName, user.Mail ?? "not set", customerId, false); + var newUserResponse = await _userService.GetOrSetUserById(id, user.DisplayName, user.Mail ?? "not set", customerId, false); if (newUserResponse is null) continue; newUserResponse.SyncedFromSharePoint = true; diff --git a/src/Server/Mappers/FunctionMapper.cs b/src/Server/Mappers/FunctionMapper.cs new file mode 100644 index 00000000..292987f4 --- /dev/null +++ b/src/Server/Mappers/FunctionMapper.cs @@ -0,0 +1,22 @@ +using Drogecode.Knrm.Oefenrooster.Server.Database.Models; +using Drogecode.Knrm.Oefenrooster.Shared.Models.Function; + +namespace Drogecode.Knrm.Oefenrooster.Server.Mappers; + +public static class FunctionMapper +{ + public static DrogeFunction ToDrogeFunction(this DbUserFunctions function) + { + return new DrogeFunction + { + Id = function.Id, + RoleId = function.RoleId, + Name = function.Name, + Order = function.Order, + TrainingTarget = function.TrainingTarget, + TrainingOnly = function.TrainingOnly, + Default = function.IsDefault, + Active = function.IsActive, + }; + } +} \ No newline at end of file diff --git a/src/Server/Mappers/PreComMapper.cs b/src/Server/Mappers/PreComMapper.cs index a5f5647b..109fc907 100644 --- a/src/Server/Mappers/PreComMapper.cs +++ b/src/Server/Mappers/PreComMapper.cs @@ -17,6 +17,7 @@ public static DbPreComForward ToDb(this PreComForward forward, Guid customerId, CreatedBy = forward.CreatedBy, }; } + public static PreComForward ToPreComForward(this DbPreComForward dbForward) { return new PreComForward diff --git a/src/Server/Services/FunctionService.cs b/src/Server/Services/FunctionService.cs index 5d152c22..8c29f203 100644 --- a/src/Server/Services/FunctionService.cs +++ b/src/Server/Services/FunctionService.cs @@ -1,16 +1,37 @@ using Drogecode.Knrm.Oefenrooster.Shared.Models.Function; using System.Diagnostics; +using Drogecode.Knrm.Oefenrooster.Server.Mappers; +using Microsoft.Extensions.Caching.Memory; namespace Drogecode.Knrm.Oefenrooster.Server.Services; public class FunctionService : IFunctionService { + private const string FUNC_BY_ID = "func_{0}"; + private readonly ILogger _logger; private readonly Database.DataContext _database; - public FunctionService(ILogger logger, Database.DataContext database) + private readonly IMemoryCache _memoryCache; + + public FunctionService(ILogger logger, Database.DataContext database, IMemoryCache memoryCache) { _logger = logger; _database = database; + _memoryCache = memoryCache; + } + + public async Task GetById(Guid customerId, Guid functionId, CancellationToken clt) + { + var memoryKey = string.Format(FUNC_BY_ID, functionId); + var function = _memoryCache.Get(memoryKey); + if (function is not null) + return function; + function = await _database.UserFunctions.Where(x => x.CustomerId == customerId && x.Id == functionId).Select(x => x.ToDrogeFunction()).FirstOrDefaultAsync(clt); + var cacheOptions = new MemoryCacheEntryOptions(); + cacheOptions.SetSlidingExpiration(TimeSpan.FromSeconds(10)); + cacheOptions.SetAbsoluteExpiration(TimeSpan.FromMinutes(1)); + _memoryCache.Set(memoryKey, function, cacheOptions); + return function; } public async Task AddFunction(DrogeFunction function, Guid customerId, CancellationToken clt) @@ -25,6 +46,7 @@ public async Task AddFunction(DrogeFunction function, Guid if (dbFunction.Order > order) order = dbFunction.Order; } + order += 10; _database.UserFunctions.Add(new Database.Models.DbUserFunctions { @@ -67,9 +89,10 @@ public async Task GetAllFunctions(Guid customerId, Ca Active = function.IsActive, }); } + result.Success = true; sw.Stop(); result.ElapsedMilliseconds = sw.ElapsedMilliseconds; return result; } -} +} \ No newline at end of file diff --git a/src/Server/Services/Interfaces/IFunctionService.cs b/src/Server/Services/Interfaces/IFunctionService.cs index 4e37d735..19cd19ce 100644 --- a/src/Server/Services/Interfaces/IFunctionService.cs +++ b/src/Server/Services/Interfaces/IFunctionService.cs @@ -4,6 +4,7 @@ namespace Drogecode.Knrm.Oefenrooster.Server.Services.Interfaces; public interface IFunctionService { + Task GetById(Guid customerId, Guid functionId, CancellationToken clt); Task AddFunction(DrogeFunction function, Guid customerId, CancellationToken clt); Task GetAllFunctions(Guid customerId, CancellationToken clt); } diff --git a/src/Server/Services/Interfaces/IScheduleService.cs b/src/Server/Services/Interfaces/IScheduleService.cs index 906cbe31..2f0451f2 100644 --- a/src/Server/Services/Interfaces/IScheduleService.cs +++ b/src/Server/Services/Interfaces/IScheduleService.cs @@ -11,7 +11,7 @@ public interface IScheduleService Task GetPlannedTrainingById(Guid customerId, Guid trainingId, CancellationToken clt); Task GetPlannedTrainingForDefaultDate(Guid customerId, DateTime date, Guid defaultId, CancellationToken token); Task PatchScheduleForUserAsync(Guid userId, Guid customerId, Training training, CancellationToken clt); - Task PatchAssignedUserAsync(Guid userId, Guid customerId, PatchAssignedUserRequest body, CancellationToken clt); + Task PatchAssignedUserAsync(Guid userId, Guid customerId, PatchAssignedUserRequest body, CancellationToken clt); Task PatchTraining(Guid customerId, PlannedTraining training, bool inRoleEditPast, CancellationToken clt); Task AddTrainingAsync(Guid customerId, PlannedTraining training, Guid trainingId, CancellationToken clt); Task GetScheduledTrainingsForUser(Guid userId, Guid customerId, DateTime? fromDate, int take, int skip, OrderAscDesc order, CancellationToken clt); diff --git a/src/Server/Services/Interfaces/IUserService.cs b/src/Server/Services/Interfaces/IUserService.cs index b6e40524..7a2cc503 100644 --- a/src/Server/Services/Interfaces/IUserService.cs +++ b/src/Server/Services/Interfaces/IUserService.cs @@ -4,8 +4,8 @@ namespace Drogecode.Knrm.Oefenrooster.Server.Services.Interfaces; public interface IUserService { Task GetAllUsers(Guid customerId, bool includeHidden, bool includeLastLogin, CancellationToken clt); - Task GetUserFromDb(Guid userId); - Task GetOrSetUserFromDb(Guid userId, string userName, string userEmail, Guid customerId, bool setLastOnline); + Task GetUserById(Guid userId, CancellationToken clt); + Task GetOrSetUserById(Guid userId, string userName, string userEmail, Guid customerId, bool setLastOnline); Task AddUser(DrogeUser user, Guid customerId); Task UpdateUser(DrogeUser user, Guid userId, Guid customerId); Task UpdateLinkUserUserForUser(UpdateLinkUserUserForUserRequest body, Guid userId, Guid customerId, CancellationToken clt); diff --git a/src/Server/Services/ScheduleService.cs b/src/Server/Services/ScheduleService.cs index 25be5a4d..bb13c51a 100644 --- a/src/Server/Services/ScheduleService.cs +++ b/src/Server/Services/ScheduleService.cs @@ -777,10 +777,12 @@ private async Task dDbTrainingToGetPlannedTrainingRe return result; } - public async Task PatchAssignedUserAsync(Guid userId, Guid customerId, PatchAssignedUserRequest body, CancellationToken clt) + public async Task PatchAssignedUserAsync(Guid userId, Guid customerId, PatchAssignedUserRequest body, CancellationToken clt) { - var result = new PatchAssignedUserResponse(); - result.IdPatched = body.TrainingId; + var result = new PatchAssignedUserResponse + { + IdPatched = body.TrainingId + }; if (body.User == null) { _logger.LogWarning("user is null {UserIsNull}", body.User == null); @@ -831,6 +833,8 @@ public async Task PatchAssignedUserAsync(Guid userId, ava.Assigned = body.User.Assigned; ava.UserFunctionId = body.User.PlannedFunctionId; ava.VehicleId = body.User.VehicleId; + clt.ThrowIfCancellationRequested(); + clt = CancellationToken.None; _database.RoosterAvailables.Update(ava); await _database.SaveChangesAsync(clt); result.Success = true; diff --git a/src/Server/Services/UserService.cs b/src/Server/Services/UserService.cs index 796b8eb9..88817612 100644 --- a/src/Server/Services/UserService.cs +++ b/src/Server/Services/UserService.cs @@ -43,18 +43,18 @@ public async Task GetAllUsers(Guid customerId, bool return result; } - public async Task GetUserFromDb(Guid userId) + public async Task GetUserById(Guid userId, CancellationToken clt) { var userObj = await _database.Users .Include(x => x.LinkedUserAsA!.Where(y => y.DeletedOn == null)) .Include(x => x.LinkedUserAsB!.Where(y => y.DeletedOn == null)) .Where(u => u.Id == userId && u.DeletedOn == null) .AsNoTracking() - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync(clt); return userObj?.ToSharedUser(false); } - public async Task GetOrSetUserFromDb(Guid userId, string userName, string userEmail, Guid customerId, bool setLastOnline) + public async Task GetOrSetUserById(Guid userId, string userName, string userEmail, Guid customerId, bool setLastOnline) { var userObj = _database.Users.FirstOrDefault(u => u.Id == userId); if (userObj is null)