Skip to content

Commit

Permalink
feat: department-summary-weekly-worker (#668)
Browse files Browse the repository at this point in the history
- [x] New feature
- [ ] Bug fix
- [ ] High impact

**Description of work:**


[AB#51133](https://statoil-proview.visualstudio.com/787035c2-8cf2-4d73-a83e-bb0e6d937eec/_workitems/edit/51133)

This takes the old report creation code and moves it to the new function
app. The ResourceOwnerReportDataCreator.cs code remains untouched.


**Testing:**
- [ ] Can be tested
- [ ] Automatic tests created / updated
- [x] Local tests are passing

I did some simple testing with hardcoded messages as the queue is not
set up yet.

**Checklist:**
- [x] Considered automated tests
- [x] Considered updating specification / documentation
- [x] Considered work items 
- [x] Considered security
- [x] Performed developer testing
- [x] Checklist finalized / ready for review
  • Loading branch information
Jonathanio123 authored Aug 22, 2024
1 parent c0cea7e commit 1008b27
Show file tree
Hide file tree
Showing 8 changed files with 484 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public Task PutDepartmentAsync(ApiResourceOwnerDepartment departments,
/// </summary>
public Task<ApiWeeklySummaryReport?> GetLatestWeeklyReportAsync(string departmentSapId,
CancellationToken cancellationToken = default);

public Task PutWeeklySummaryReportAsync(string departmentSapId, ApiWeeklySummaryReport report,
CancellationToken cancellationToken = default);
}

#region Models
Expand Down Expand Up @@ -71,7 +74,7 @@ public record ApiWeeklySummaryReport
public record ApiPersonnelMoreThan100PercentFTE
{
public string FullName { get; set; } = "-";
public int FTE { get; set; } = -1;
public double FTE { get; set; } = -1;
}

public record ApiEndingPosition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json;
using Fusion.Resources.Functions.Common.Extensions;
using Fusion.Resources.Functions.Common.Integration.Http;

namespace Fusion.Resources.Functions.Common.ApiClients;
Expand Down Expand Up @@ -47,12 +48,10 @@ public async Task PutDepartmentAsync(ApiResourceOwnerDepartment department,
public async Task<ApiWeeklySummaryReport?> GetLatestWeeklyReportAsync(string departmentSapId,
CancellationToken cancellationToken = default)
{
// Get the date of the last monday or today if today is monday
// So the weekly report is based on the week that has passed
var lastMonday = GetCurrentOrLastMondayDate();
var lastMonday = DateTime.UtcNow.GetPreviousWeeksMondayDate();

var queryString =
$"summary-reports/{departmentSapId}/weekly?$filter=Period eq '{lastMonday.Date:O}'&$top=1";
$"resource-owners-summary-reports/{departmentSapId}/weekly?$filter=Period eq '{lastMonday.Date:O}'&$top=1";

using var response = await summaryClient.GetAsync(queryString, cancellationToken);
if (!response.IsSuccessStatusCode)
Expand All @@ -65,26 +64,13 @@ public async Task PutDepartmentAsync(ApiResourceOwnerDepartment department,
cancellationToken: cancellationToken))?.Items?.FirstOrDefault();
}

private static DateTime GetCurrentOrLastMondayDate()
public async Task PutWeeklySummaryReportAsync(string departmentSapId, ApiWeeklySummaryReport report,
CancellationToken cancellationToken = default)
{
var date = DateTime.UtcNow;
switch (date.DayOfWeek)
{
case DayOfWeek.Sunday:
return date.AddDays(-6);
case DayOfWeek.Monday:
return date;
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
case DayOfWeek.Thursday:
case DayOfWeek.Friday:
case DayOfWeek.Saturday:
default:
{
var daysUntilMonday = (int)date.DayOfWeek - 1;

return date.AddDays(-daysUntilMonday);
}
}
using var body = new JsonContent(JsonSerializer.Serialize(report, jsonSerializerOptions));

// Error logging is handled by http middleware => FunctionHttpMessageHandler
using var _ = await summaryClient.PutAsync($"resource-owners-summary-reports/{departmentSapId}/weekly", body,
cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Fusion.Resources.Functions.Common.Extensions;

public static class DateTimeExtensions
{
/// <summary>
/// Returns a new DateTime object with the date set to monday last week.
/// </summary>
public static DateTime GetPreviousWeeksMondayDate(this DateTime date)
{
switch (date.DayOfWeek)
{
case DayOfWeek.Sunday:
return date.AddDays(-6);
case DayOfWeek.Monday:
return date.AddDays(-7);
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
case DayOfWeek.Thursday:
case DayOfWeek.Friday:
case DayOfWeek.Saturday:
default:
{
// Calculate days until previous monday
// Go one week back and then remove the days until the monday
var daysUntilLastWeeksMonday = (1 - (int)date.DayOfWeek) - 7;

return date.AddDays(daysUntilLastWeeksMonday);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"environment": "pr",
"disabledFunctions": [
"department-resource-owner-sync",
"weekly-report-sender"
"weekly-report-sender",
"weekly-department-summary-worker"
]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Fusion.Integration.Profile;
using Fusion.Resources.Functions.Common.ApiClients;
using Fusion.Resources.Functions.Common.Extensions;
using Fusion.Summary.Functions.ReportCreator;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.ServiceBus;
using Microsoft.Extensions.Logging;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace Fusion.Summary.Functions.Functions;

public class WeeklyDepartmentSummaryWorker
{
private readonly IResourcesApiClient _resourceClient;
private readonly ISummaryApiClient _summaryApiClient;
private readonly ILogger<WeeklyDepartmentSummaryWorker> _logger;

public WeeklyDepartmentSummaryWorker(IResourcesApiClient resourceClient, ILogger<WeeklyDepartmentSummaryWorker> logger, ISummaryApiClient summaryApiClient)
{
_resourceClient = resourceClient;
_logger = logger;
_summaryApiClient = summaryApiClient;
}

[FunctionName("weekly-department-summary-worker")]
public async Task RunAsync(
[ServiceBusTrigger("%department_summary_weekly_queue%", Connection = "AzureWebJobsServiceBus")]
ServiceBusReceivedMessage message, ServiceBusMessageActions messageReceiver)
{
try
{
var dto = await JsonSerializer.DeserializeAsync<ApiResourceOwnerDepartment>(message.Body.ToStream());

if (string.IsNullOrEmpty(dto.FullDepartmentName))
throw new Exception("FullDepartmentIdentifier not valid.");

await CreateAndStoreReportAsync(dto);
}
catch (Exception e)
{
_logger.LogError(e, "Error while processing message");
throw;
}
finally
{
// Complete the message regardless of outcome.
await messageReceiver.CompleteMessageAsync(message);
}
}

private async Task CreateAndStoreReportAsync(ApiResourceOwnerDepartment message)
{
var departmentRequests = (await _resourceClient.GetAllRequestsForDepartment(message.FullDepartmentName)).ToList();

var departmentPersonnel = (await _resourceClient.GetAllPersonnelForDepartment(message.FullDepartmentName))
.Where(per =>
per.AccountType != FusionAccountType.Consultant.ToString() &&
per.AccountType != FusionAccountType.External.ToString())
.ToList();

// Check if the department has personnel, abort if not
if (departmentPersonnel.Count == 0)
{
_logger.LogInformation("Department contains no personnel, no need to store report");
return;
}

var report = await BuildSummaryReportAsync(departmentPersonnel, departmentRequests, message.DepartmentSapId);

await _summaryApiClient.PutWeeklySummaryReportAsync(message.DepartmentSapId, report);
}


private static Task<ApiWeeklySummaryReport> BuildSummaryReportAsync(
List<IResourcesApiClient.InternalPersonnelPerson> personnel,
List<IResourcesApiClient.ResourceAllocationRequest> requests,
string departmentSapId)
{
var report = new ApiWeeklySummaryReport()
{
Period = DateTime.UtcNow.GetPreviousWeeksMondayDate(),
DepartmentSapId = departmentSapId,
PositionsEnding = ResourceOwnerReportDataCreator
.GetPersonnelPositionsEndingWithNoFutureAllocation(personnel)
.Select(ep => new ApiEndingPosition()
{
EndDate = ep.EndDate.GetValueOrDefault(DateTime.MinValue),
FullName = ep.FullName
})
.ToArray(),
PersonnelMoreThan100PercentFTE = ResourceOwnerReportDataCreator
.GetPersonnelAllocatedMoreThan100Percent(personnel)
.Select(ep => new ApiPersonnelMoreThan100PercentFTE()
{
FullName = ep.FullName,
FTE = ep.TotalWorkload
})
.ToArray(),
NumberOfPersonnel = ResourceOwnerReportDataCreator.GetTotalNumberOfPersonnel(personnel).ToString(),
CapacityInUse = ResourceOwnerReportDataCreator.GetCapacityInUse(personnel).ToString(),
NumberOfRequestsLastPeriod = ResourceOwnerReportDataCreator.GetNumberOfRequestsLastWeek(requests).ToString(),
NumberOfOpenRequests = ResourceOwnerReportDataCreator.GetNumberOfOpenRequests(requests).ToString(),
NumberOfRequestsStartingInLessThanThreeMonths = ResourceOwnerReportDataCreator.GetNumberOfRequestsStartingInLessThanThreeMonths(requests).ToString(),
NumberOfRequestsStartingInMoreThanThreeMonths = ResourceOwnerReportDataCreator.GetNumberOfRequestsStartingInMoreThanThreeMonths(requests).ToString(),
AverageTimeToHandleRequests = ResourceOwnerReportDataCreator.GetAverageTimeToHandleRequests(requests).ToString(),
AllocationChangesAwaitingTaskOwnerAction = ResourceOwnerReportDataCreator.GetAllocationChangesAwaitingTaskOwnerAction(requests).ToString(),
ProjectChangesAffectingNextThreeMonths = ResourceOwnerReportDataCreator.CalculateDepartmentChangesLastWeek(personnel).ToString()
};

return Task.FromResult(report);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="5.15.1"/>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.2.0" />
</ItemGroup>
<ItemGroup>
Expand Down
Loading

0 comments on commit 1008b27

Please sign in to comment.