From 999d3d24a7e5e74744d096497603337e1a523f14 Mon Sep 17 00:00:00 2001 From: pshahsancsoft Date: Wed, 28 Aug 2024 13:56:33 -0400 Subject: [PATCH 1/3] Staff Hours ProtoType --- .../Emails/EmployeeHoursEmail.cs | 62 +++++++++++++++++++ .../Enumerations/EmailMessage.cs | 3 +- .../Views/Emails/HTML/EmployeeHours.cshtml | 37 +++++++++++ .../Views/Emails/Text/EmployeeHours.cshtml | 47 ++++++++++++++ .../EmailTemplateTestControllerV1.cs | 20 ++++++ src/dotnet/HQ.Server/Program.cs | 6 ++ .../HQ.Server/Services/EmailMessageService.cs | 53 ++++++++++++++++ 7 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 src/dotnet/HQ.Abstractions/Emails/EmployeeHoursEmail.cs create mode 100644 src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml create mode 100644 src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml diff --git a/src/dotnet/HQ.Abstractions/Emails/EmployeeHoursEmail.cs b/src/dotnet/HQ.Abstractions/Emails/EmployeeHoursEmail.cs new file mode 100644 index 00000000..8de8f960 --- /dev/null +++ b/src/dotnet/HQ.Abstractions/Emails/EmployeeHoursEmail.cs @@ -0,0 +1,62 @@ + +using HQ.Abstractions.Staff; +using HQ.Abstractions.Times; + +namespace HQ.Abstractions.Emails +{ + public class EmployeeHoursEmail : NotificationEmail + { + //This will contain staff name and hours + public List Staff { get; set; } = null!; + public DateOnly Date { get; set; } + + public DateOnly PeriodBegin { get; set; } + public DateOnly PeriodEnd { get; set; } + + public static new EmployeeHoursEmail Sample = new() + { + Heading = "Staff Hours", + Message = "Staff with red signify they have less working hours than expected", + ButtonLabel = "Open HQ", + ButtonUrl = new Uri("http://hq.localhost:4200/dashboard"), + Date = new DateOnly(2024, 7, 17), + PeriodBegin = new DateOnly(2024, 7, 17), + PeriodEnd = new DateOnly(2024, 7, 23), + + Staff = new List{ + new StaffHoursModel{ + StaffName = "Amr", + HoursLastWeek= 0, + HoursLastMonth = 0, + HoursThisMonth = 4, + MissingHours = true + }, + new StaffHoursModel{ + StaffName = "Ryan", + HoursLastWeek= 5, + HoursLastMonth = 6, + HoursThisMonth = 7, + LessThanExpectedHours = true + }, + new StaffHoursModel{ + StaffName = "Pri", + HoursLastWeek= 8, + HoursLastMonth = 9, + HoursThisMonth = 10 + } + } + + }; + public class StaffHoursModel + { + public decimal HoursLastWeek { get; set; } + public decimal HoursLastMonth { get; set; } + public decimal HoursThisMonth { get; set; } + public string? StaffName { get; set; } + public bool MissingHours { get; set; } + public bool LessThanExpectedHours { get; set; } + public int WorkHours { get; set; } + + } + } +} \ No newline at end of file diff --git a/src/dotnet/HQ.Abstractions/Enumerations/EmailMessage.cs b/src/dotnet/HQ.Abstractions/Enumerations/EmailMessage.cs index 267bc367..b79da451 100644 --- a/src/dotnet/HQ.Abstractions/Enumerations/EmailMessage.cs +++ b/src/dotnet/HQ.Abstractions/Enumerations/EmailMessage.cs @@ -4,5 +4,6 @@ public enum EmailMessage { Notification = 1, RejectTimeEntry = 2, - UpdatedPlanningPoints = 3 + UpdatedPlanningPoints = 3, + EmployeeHours = 4 } \ No newline at end of file diff --git a/src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml b/src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml new file mode 100644 index 00000000..32c9c708 --- /dev/null +++ b/src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml @@ -0,0 +1,37 @@ +@model HQ.Abstractions.Emails.EmployeeHoursEmail +@{ + Layout = "_LayoutEmailNotificationHTML"; +} + +
Dates: @Model.PeriodBegin - @Model.PeriodEnd
+
+ + + StaffName + Hours Last Week + Hours This Month + Hours Last Month + + @foreach (var staff in Model.Staff) + { + @if (staff.LessThanExpectedHours || staff.MissingHours) + { + + @staff.StaffName + @staff.HoursLastWeek + @staff.HoursThisMonth + @staff.HoursLastMonth + + } + else + { + + @staff.StaffName + @staff.HoursLastWeek + @staff.HoursThisMonth + @staff.HoursLastMonth + + } + } + + \ No newline at end of file diff --git a/src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml b/src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml new file mode 100644 index 00000000..7c74815a --- /dev/null +++ b/src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml @@ -0,0 +1,47 @@ +@model HQ.Abstractions.Emails.EmployeeHoursEmail +@if (!String.IsNullOrEmpty(Model.Heading)) +{ + @Model.Heading + --- + +} + +@if (!String.IsNullOrEmpty(Model.Message)) +{ + @Model.Message + +} + +Dates: @Model.PeriodBegin - @Model.PeriodEnd + + + @foreach (var staff in @Model.Staff) + { + @if (staff.LessThanExpectedHours || staff.MissingHours) + { + + + + + + + + + } + else + { + + + + + + + } + + } + + @if (!String.IsNullOrEmpty(Model.ButtonLabel) && Model.ButtonUrl != null) + { + @Model.ButtonLabel: @Model.ButtonUrl + } +
@staff.StaffName@staff.HoursLastWeek@staff.HoursThisMonth@staff.HoursLastMonth
@staff.StaffName@staff.HoursLastWeek@staff.HoursThisMonth@staff.HoursLastMonth
\ No newline at end of file diff --git a/src/dotnet/HQ.Server/Controllers/EmailTemplateTestControllerV1.cs b/src/dotnet/HQ.Server/Controllers/EmailTemplateTestControllerV1.cs index 83436522..2a335f75 100644 --- a/src/dotnet/HQ.Server/Controllers/EmailTemplateTestControllerV1.cs +++ b/src/dotnet/HQ.Server/Controllers/EmailTemplateTestControllerV1.cs @@ -103,6 +103,26 @@ public Task UpdatedPlanningPointsSendEmail([FromForm] string to, C _emailService.SendEmail(EmailMessage.UpdatedPlanningPoints, UpdatedPlanningPointsEmail.Sample, to, "Updated Points Test", System.Net.Mail.MailPriority.Low, null, ct) .ToActionResult(new HQResultEndpointProfile()); + [HttpGet(nameof(StaffHoursText))] + [ProducesResponseType(StatusCodes.Status200OK, "text/plain")] + public Task StaffHoursText(CancellationToken ct = default) => + GetEmailTemplate(EmailMessageOutput.Text, EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, ct); + + [HttpGet(nameof(StaffHoursMJML))] + [ProducesResponseType(StatusCodes.Status200OK, "text/plain")] + public Task StaffHoursMJML(CancellationToken ct = default) => + GetEmailTemplate(EmailMessageOutput.MJML, EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, ct); + + [HttpGet(nameof(StaffHoursHTML))] + [ProducesResponseType(StatusCodes.Status200OK, "text/html")] + public Task StaffHoursHTML(CancellationToken ct = default) => + GetEmailTemplate(EmailMessageOutput.HTML, EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, ct); + + [HttpPost(nameof(StaffHoursSendEmail))] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public Task StaffHoursSendEmail([FromForm] string to, CancellationToken ct = default) => + _emailService.SendEmail(EmailMessage.EmployeeHours, EmployeeHoursEmail.Sample, to, "Staff Hours Test", System.Net.Mail.MailPriority.Low, null, ct) + .ToActionResult(new HQResultEndpointProfile()); private async Task GetEmailTemplate(EmailMessageOutput output, EmailMessage emailMessage, T model, CancellationToken ct = default) where T : BaseEmail { var request = new GetEmailTemplateV1.Request() diff --git a/src/dotnet/HQ.Server/Program.cs b/src/dotnet/HQ.Server/Program.cs index 3880715e..83a3e454 100644 --- a/src/dotnet/HQ.Server/Program.cs +++ b/src/dotnet/HQ.Server/Program.cs @@ -296,6 +296,12 @@ Cron.Weekly(DayOfWeek.Monday, 12), recurringJobOptions); +recurringJobManager.AddOrUpdate( + nameof(EmailMessageService.SendEmployeeHoursEmail), + (t) => t.SendEmployeeHoursEmail(CancellationToken.None), + Cron.Weekly(DayOfWeek.Monday, 12), + recurringJobOptions); + // Friday morning recurringJobManager.AddOrUpdate( nameof(HolidayServiceV1.BackgroundAutoGenerateHolidayTimeEntryV1), diff --git a/src/dotnet/HQ.Server/Services/EmailMessageService.cs b/src/dotnet/HQ.Server/Services/EmailMessageService.cs index 97963330..b9e8ef7c 100644 --- a/src/dotnet/HQ.Server/Services/EmailMessageService.cs +++ b/src/dotnet/HQ.Server/Services/EmailMessageService.cs @@ -7,6 +7,7 @@ using HQ.Abstractions.EmailTemplates; using HQ.Abstractions.Enumerations; using HQ.Abstractions.Services; +using HQ.Abstractions.Times; using HQ.Server.Data; using Microsoft.EntityFrameworkCore; @@ -302,5 +303,57 @@ public async Task SendUpdatedPlanningPointsEmail(Guid staffId, Guid modifiedBySt await SendEmail(EmailMessage.UpdatedPlanningPoints, model, staff.Email, "[HQ] Planning Points Updated", MailPriority.High, null, ct); } } + public async Task SendEmployeeHoursEmail(CancellationToken ct) + { + var date = DateOnly.FromDateTime(DateTime.UtcNow); + + var staff = await _context.Staff + .AsNoTracking() + .Where(t => t.EndDate == null) + .Select(t => new EmployeeHoursEmail.StaffHoursModel() + { + HoursLastWeek = t.Times.Where(x => x.Date >= date.GetPeriodStartDate(Period.LastWeek) && x.Date <= date.GetPeriodEndDate(Period.LastWeek)).Sum(x => x.Hours), + HoursLastMonth = t.Times.Where(x => x.Date >= date.GetPeriodStartDate(Period.LastMonth) && x.Date <= date.GetPeriodEndDate(Period.LastMonth)).Sum(x => x.Hours), + HoursThisMonth = t.Times.Where(x => x.Date >= date.GetPeriodStartDate(Period.Month) && x.Date <= date.GetPeriodEndDate(Period.Month)).Sum(x => x.Hours), + StaffName = t.Name, + WorkHours = t.WorkHours + }) + .OrderByDescending(t => t.HoursLastWeek) + .ToListAsync(ct); + foreach (var employee in staff) + { + employee.MissingHours = employee.HoursLastWeek == 0 || employee.HoursLastMonth == 0 || employee.HoursLastMonth == 0; + employee.LessThanExpectedHours = employee.HoursLastWeek < employee.WorkHours; + } + + var managersToNotify = await _context.Projects + .AsNoTracking() + .Where(t => t.EndDate == null) + .Select(t => t.ProjectManager!.Email) + .Distinct() + .ToListAsync(ct); + + var model = new EmployeeHoursEmail() + { + Date = date, + PeriodBegin = date.GetPeriodStartDate(Period.LastWeek), + PeriodEnd = date.GetPeriodEndDate(Period.LastWeek), + ButtonLabel = "Open HQ", + ButtonUrl = _options.CurrentValue.WebUrl, + Staff = staff + }; + foreach (var manager in managersToNotify) + { + if (manager != null) + { + + await SendEmail(EmailMessage.EmployeeHours, model, manager, "[HQ] Staff Hours", MailPriority.High, null, ct); + } + else + { + return; + } + } + } } } \ No newline at end of file From 880d6609e6fd4992d6e9835699cbde2e5994deb8 Mon Sep 17 00:00:00 2001 From: pshahsancsoft Date: Wed, 28 Aug 2024 15:59:09 -0400 Subject: [PATCH 2/3] Updated --- .../Views/Emails/HTML/EmployeeHours.cshtml | 10 +++++----- .../Views/Emails/Text/EmployeeHours.cshtml | 8 +++++++- src/dotnet/HQ.Server/Program.cs | 2 +- .../HQ.Server/Services/EmailMessageService.cs | 16 ++++------------ 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml b/src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml index 32c9c708..46464e72 100644 --- a/src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml +++ b/src/dotnet/HQ.Email/Views/Emails/HTML/EmployeeHours.cshtml @@ -7,16 +7,16 @@ - StaffName - Hours Last Week - Hours This Month - Hours Last Month + Staff + Last Week + This Month + Last Month @foreach (var staff in Model.Staff) { @if (staff.LessThanExpectedHours || staff.MissingHours) { - + @staff.StaffName @staff.HoursLastWeek @staff.HoursThisMonth diff --git a/src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml b/src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml index 7c74815a..6f7d37ae 100644 --- a/src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml +++ b/src/dotnet/HQ.Email/Views/Emails/Text/EmployeeHours.cshtml @@ -15,12 +15,18 @@ Dates: @Model.PeriodBegin - @Model.PeriodEnd + + + + + + @foreach (var staff in @Model.Staff) { @if (staff.LessThanExpectedHours || staff.MissingHours) { - + diff --git a/src/dotnet/HQ.Server/Program.cs b/src/dotnet/HQ.Server/Program.cs index 83a3e454..885f32be 100644 --- a/src/dotnet/HQ.Server/Program.cs +++ b/src/dotnet/HQ.Server/Program.cs @@ -299,7 +299,7 @@ recurringJobManager.AddOrUpdate( nameof(EmailMessageService.SendEmployeeHoursEmail), (t) => t.SendEmployeeHoursEmail(CancellationToken.None), - Cron.Weekly(DayOfWeek.Monday, 12), + "15 12 * * 1", recurringJobOptions); // Friday morning diff --git a/src/dotnet/HQ.Server/Services/EmailMessageService.cs b/src/dotnet/HQ.Server/Services/EmailMessageService.cs index b9e8ef7c..1a2c5968 100644 --- a/src/dotnet/HQ.Server/Services/EmailMessageService.cs +++ b/src/dotnet/HQ.Server/Services/EmailMessageService.cs @@ -318,17 +318,17 @@ public async Task SendEmployeeHoursEmail(CancellationToken ct) StaffName = t.Name, WorkHours = t.WorkHours }) - .OrderByDescending(t => t.HoursLastWeek) + .OrderBy(t => t.HoursLastWeek) .ToListAsync(ct); foreach (var employee in staff) { - employee.MissingHours = employee.HoursLastWeek == 0 || employee.HoursLastMonth == 0 || employee.HoursLastMonth == 0; + employee.MissingHours = employee.HoursLastWeek == 0; employee.LessThanExpectedHours = employee.HoursLastWeek < employee.WorkHours; } var managersToNotify = await _context.Projects .AsNoTracking() - .Where(t => t.EndDate == null) + .Where(t => t.EndDate == null && t.ProjectManager!.Email != null) .Select(t => t.ProjectManager!.Email) .Distinct() .ToListAsync(ct); @@ -344,15 +344,7 @@ public async Task SendEmployeeHoursEmail(CancellationToken ct) }; foreach (var manager in managersToNotify) { - if (manager != null) - { - - await SendEmail(EmailMessage.EmployeeHours, model, manager, "[HQ] Staff Hours", MailPriority.High, null, ct); - } - else - { - return; - } + await SendEmail(EmailMessage.EmployeeHours, model, manager!, "[HQ] Staff Hours", MailPriority.High, null, ct); } } } From 2eb37d0f2b76a786359ab23e40b79e44e96ccb84 Mon Sep 17 00:00:00 2001 From: pshahsancsoft Date: Thu, 26 Sep 2024 10:20:37 -0400 Subject: [PATCH 3/3] pr change --- src/dotnet/HQ.Server/Services/EmailMessageService.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dotnet/HQ.Server/Services/EmailMessageService.cs b/src/dotnet/HQ.Server/Services/EmailMessageService.cs index 1a2c5968..187d49e0 100644 --- a/src/dotnet/HQ.Server/Services/EmailMessageService.cs +++ b/src/dotnet/HQ.Server/Services/EmailMessageService.cs @@ -344,7 +344,10 @@ public async Task SendEmployeeHoursEmail(CancellationToken ct) }; foreach (var manager in managersToNotify) { - await SendEmail(EmailMessage.EmployeeHours, model, manager!, "[HQ] Staff Hours", MailPriority.High, null, ct); + if (manager != null) + { + await SendEmail(EmailMessage.EmployeeHours, model, manager, "[HQ] Staff Hours", MailPriority.High, null, ct); + } } } }
StaffLast WeekThis MonthLast Month
@staff.StaffName @staff.HoursLastWeek @staff.HoursThisMonth