Skip to content

Commit

Permalink
Cleanup and fixes to notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
aleklundeq committed Dec 12, 2023
1 parent e953b7d commit ba37e11
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ public class ApiInstanceSnapshot
}
}

public enum ChangeType
public class ChangeType
{
PositionInstanceCreated,
PersonAssignedToPosition,
PositionInstanceAllocationStateChanged,
PositionInstanceAppliesToChanged,
PositionInstanceAppliesFromChanged,
PositionInstanceParentPositionIdChanged,
PositionInstancePercentChanged,
PositionInstanceLocationChanged
public static string PositionInstanceCreated = "PositionInstanceCreated";
public static string PersonAssignedToPosition = "PersonAssignedToPosition";
public static string PositionInstanceAllocationStateChanged = "PositionInstanceAllocationStateChanged";
public static string PositionInstanceAppliesToChanged = "PositionInstanceAppliesToChanged";
public static string PositionInstanceAppliesFromChanged = "PositionInstanceAppliesFromChanged";
public static string PositionInstanceParentPositionIdChanged = "PositionInstanceParentPositionIdChanged";
public static string PositionInstancePercentChanged = "PositionInstancePercentChanged";
public static string PositionInstanceLocationChanged = "PositionInstanceLocationChanged";
}

#endregion
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ScheduledReportContentBuilderFunction
private readonly INotificationApiClient _notificationsClient;
private readonly IResourcesApiClient _resourceClient;
private readonly IOrgClient _orgClient;
private static readonly string formatDoubleToHaveOneDecimal = "F1";

public ScheduledReportContentBuilderFunction(ILogger<ScheduledReportContentBuilderFunction> logger,
IResourcesApiClient resourcesApiClient,
Expand Down Expand Up @@ -58,7 +59,7 @@ public async Task RunAsync(
switch (dto.Role)
{
case NotificationRoleType.ResourceOwner:
await BuildContentForResourceOwner(azureUniqueId, dto.FullDepartment);
await BuildContentForResourceOwner(azureUniqueId, dto.FullDepartment, dto.DepartmentSapId);
break;
case NotificationRoleType.TaskOwner:
await BuildContentForTaskOwner(azureUniqueId);
Expand Down Expand Up @@ -89,91 +90,65 @@ private async Task BuildContentForTaskOwner(Guid azureUniqueId)
throw new NotImplementedException();
}

private async Task BuildContentForResourceOwner(Guid azureUniqueId, string fullDepartment)
private async Task BuildContentForResourceOwner(Guid azureUniqueId, string fullDepartment, string departmentSapId)
{
var threeMonthsFuture = DateTime.UtcNow.AddMonths(3);
var today = DateTime.UtcNow;
var sevenDaysAgo = DateTime.UtcNow.AddDays(-7);

// Get all requests for specific Department regardsless of state
// Get all requests for department regardsless of state
var departmentRequests = await _resourceClient.GetAllRequestsForDepartment(fullDepartment);

// Get all the personnel for the specific department
// Get all the personnel for the department
var personnelForDepartment = await _resourceClient.GetAllPersonnelForDepartment(fullDepartment);
// Adding leave
personnelForDepartment = await GetPersonnelLeave(personnelForDepartment);


//1.Number of personnel:
//number of personnel in the department(one specific org unit)
// Hvordan finne?
// Hent ut alle ressursene for en gitt avdeling
// OK?
//1.Number of personnel
var numberOfPersonnel = personnelForDepartment.Count();

//2.Capacity in use:
//capacity in use by %.
//Calculated by total current workload for all personnel / (100 % workload x number of personnel - (total % leave)),
//a.e.g. 10 people in department: 800 % current workload / (1000 % -120 % leave) = 91 % capacity in use
// OK - Inkluderer nå leave..
var percentageOfTotalCapacity = FindTotalCapacityIncludingLeave(personnelForDepartment.ToList());

// 3.New requests last week:
// number of requests received last 7 days
// Notat: En request kan ha blitt opprettet for 7 dager siden, men ikke oversendt til ressurseiere - Det kan være
// Inkluderer foreløpig alle requestene uavhengig av hvilken state de er
var numberOfRequestsLastWeek = departmentRequests.Where(req => req.Created > sevenDaysAgo && !req.IsDraft).Count();


// 3.New requests last week (7 days)
var numberOfRequestsLastWeek = departmentRequests.Count(req => !req.Type.Equals("ResourceOwnerChange") && req.Created > sevenDaysAgo && !req.IsDraft);

//4.Open request:
//number of requests with no proposed candidate
// Only to include those requests which have state approval (this means that the resource owner needs to process the requests in some way)
// OK? - Må sjekkes og finne noen som har approval...
var totalNumberOfOpenRequests = departmentRequests.Count(req => !req.HasProposedPerson && !req.State.Contains(RequestState.completed.ToString()));
//4.Open request (no proposedPerson)
var totalNumberOfOpenRequests = departmentRequests.Count(req => !req.Type.Equals("ResourceOwnerChange") && !req.HasProposedPerson && !req.State.Equals("completed", StringComparison.OrdinalIgnoreCase));


//5.Requests with start-date < 3 months:
//number of requests with start date within less than 3 months
// Filter to only inlclude the ones that have start-date in less than 3 months and start-date after today and is not complete and has no proposedPerson assigned to them
//5.Requests with start-date < 3 months
var numberOfDepartmentRequestWithLessThanThreeMonthsBeforeStartAndNoNomination = departmentRequests
.Count(x => x.OrgPositionInstance != null &&
x.State != null &&
!x.State.Contains(RequestState.completed.ToString()) &&
!x.State.Equals("completed") &&
(x.OrgPositionInstance.AppliesFrom < threeMonthsFuture &&
x.OrgPositionInstance.AppliesFrom > today) && !x.HasProposedPerson);


//6.Requests with start-date > 3 months:
//number of requests with start date later than next 3 months
// Filter to only include the ones that have start-date in more than 3 months AND state not completed
var numberOfDepartmentRequestWithMoreThanThreeMonthsBeforeStart = departmentRequests
.Count(x => x.OrgPositionInstance != null &&
x.State != null &&
!x.State.Contains(RequestState.completed.ToString()) &&
!x.State.Contains("completed") &&
x.OrgPositionInstance.AppliesFrom > threeMonthsFuture);

// TODO: MÅ TESTES OG VERIFISERES
//7.Average time to handle request:
//average number of days from request created/ sent to candidate is proposed - last 6 months
//7.Average time to handle request (last 3 months):
var averageTimeToHandleRequest = CalculateAverageTimeToHandleRequests(departmentRequests);

// TODO: GJENSTÅR
//8.Allocation changes awaiting task owner action:
//number of allocation changes made by resource owner awaiting task owner action
//Må hente ut alle posisjoner som har ressurser for en gitt avdeling og sjekke på om det er gjort endringer her den siste tiden
var numberOfAllocationchangesAwaitingTaskOwnerAction = GetchangesAwaitingTaskOwnerAction(departmentRequests);

//9.Project changes affecting next 3 months: MÅ TESTES OG VERIFISERES
//9.Project changes affecting next 3 months
//number of project changes(changes initiated by project / task) with a change affecting the next 3 months
var numberOfChangesAffectingNextThreeMonths = GetAllChangesForResourceDepartment(personnelForDepartment);

//10.Allocations ending soon with no future allocation:
//list of allocations ending within next 3 months where the person allocated does not continue in the position(i.e.no future splits with the same person allocated)
//10.Allocations ending soon with no future allocation
var listOfPersonnelWithoutFutureAllocations = FilterPersonnelWithoutFutureAllocations(personnelForDepartment);

//11.Personnel with more than 100 % workload: -OK
//(as in current pilot, but remove "FTE") list of persons with total allocation > 100 %, total % workload should be visible after person name
// TODO: Fiks formatering og oppdeling av innhold her
//11.Personnel with more than 100 % workload
var listOfPersonnelsWithMoreThan100Percent = personnelForDepartment.Where(p =>
p.PositionInstances.Where(pos => pos.IsActive).Select(pos => pos.Workload).Sum() > 100);
var listOfPersonnelForDepartmentWithMoreThan100Percent =
Expand All @@ -196,7 +171,7 @@ private async Task BuildContentForResourceOwner(Guid azureUniqueId, string fullD
PersonnelPositionsEndingWithNoFutureAllocation = listOfPersonnelWithoutFutureAllocations,
PersonnelAllocatedMoreThan100Percent = listOfPersonnelForDepartmentWithMoreThan100Percent
},
fullDepartment);
fullDepartment, departmentSapId);

var sendNotification = await _notificationsClient.SendNotification(
new SendNotificationsRequest()
Expand Down Expand Up @@ -279,10 +254,10 @@ private int GetAllChangesForResourceDepartment(IEnumerable<InternalPersonnelPers

var changeLogForPersonnel = _orgClient.GetChangeLog(instance.Project.Id.ToString(), instance.PositionId.ToString(), instance.InstanceId.ToString());
var totalChanges = changeLogForPersonnel.Result.Events
.Where(ev => ev.ChangeType != ChangeType.PositionInstanceCreated.ToString()
&& ev.ChangeType != ChangeType.PersonAssignedToPosition.ToString()
&& ev.ChangeType != ChangeType.PositionInstanceAllocationStateChanged.ToString()
&& ev.ChangeType != ChangeType.PositionInstanceParentPositionIdChanged.ToString()
.Where(ev => ev.ChangeType != ChangeType.PositionInstanceCreated
&& ev.ChangeType != ChangeType.PersonAssignedToPosition
&& ev.ChangeType != ChangeType.PositionInstanceAllocationStateChanged
&& ev.ChangeType != ChangeType.PositionInstanceParentPositionIdChanged
&& (ev.ChangeType.Equals(ChangeType.PositionInstanceAppliesToChanged) && ev.Instance.AppliesTo < threeMonths));


Expand All @@ -292,39 +267,38 @@ private int GetAllChangesForResourceDepartment(IEnumerable<InternalPersonnelPers
}

public int GetchangesAwaitingTaskOwnerAction(IEnumerable<ResourceAllocationRequest> listOfRequests)
=> listOfRequests.Where((req => req.Type.Equals("ResourceOwnerChange"))).Where(req => req.Workflow.Steps.Any(step => !step.IsCompleted && step.Name.Equals("Accept"))).ToList().Count();
=> listOfRequests.Where((req => req.Type.Equals("ResourceOwnerChange"))).Where(req => req.Workflow.Steps.Any(step => step.Name.Equals("Accept") && !step.IsCompleted)).ToList().Count();


private double CalculateAverageTimeToHandleRequests(IEnumerable<ResourceAllocationRequest> listOfRequests)
{
// How to calculate:
// for each request:
// Find the workflow "created" and then find the date
// This should mean that task owner have created and sent the request to resource owner
// Find the workflow "proposal" and then find the date
// This should mean that the resource owner have done their bit
// TODO: Maybe we need to consider other states
// FIXME: We may need to filter out requests that are sent from resource-side (aka ResourceOwnerChange)


double days = 0;
/* How to calculate:
*
* Find the workflow "created" and then find the date
* This should mean that task owner have created and sent the request to resource owner
* Find the workflow "proposal" and then find the date
* This should mean that the resource owner have done their bit
* TODO: Maybe we need to consider other states
*/

double days = 0.0;
int requestsHandledByResourceOwner = 0;
double totalNumberOfDays = 0.0;

var threeMonthsAgo = DateTime.UtcNow.AddMonths(-3);

var requestsLastThreemonths = listOfRequests
var requestsLastThreeMonthsWithoutResourceOwnerChangeRequest = listOfRequests
.Where(req => req.Created > threeMonthsAgo)
.Where(r => r.Workflow is not null)
.Where(r => r.Workflow.Steps is not null);
.Where(r => r.Workflow.Steps is not null).Where((req => !req.Type.Equals("ResourceOwnerChange")));

foreach (var request in requestsLastThreemonths)
foreach (var request in requestsLastThreeMonthsWithoutResourceOwnerChangeRequest)
{
if (request.State.Equals(RequestState.created.ToString()))
if (request.State.Equals("created"))
continue;

var dateForCreation = request.Workflow.Steps.FirstOrDefault(step => step.IsCompleted && step.Name.Equals("Created"))?.Completed.Value.DateTime;
var dateForApproval = request.Workflow.Steps.FirstOrDefault(step => step.IsCompleted && step.Name.Equals("Proposed"))?.Completed.Value.DateTime;
var dateForCreation = request.Workflow.Steps.FirstOrDefault(step => step.Name.Equals("Created") && step.IsCompleted)?.Completed.Value.DateTime;
var dateForApproval = request.Workflow.Steps.FirstOrDefault(step => step.Name.Equals("Proposed") && step.IsCompleted)?.Completed.Value.DateTime;
if (dateForCreation != null && dateForApproval != null)
{
requestsHandledByResourceOwner++;
Expand Down Expand Up @@ -374,14 +348,10 @@ private PersonnelContent CreatePersonnelWithTBEContent(InternalPersonnelPerson p
}

private static AdaptiveCard ResourceOwnerAdaptiveCardBuilder(ResourceOwnerAdaptiveCardData cardData,
string departmentIdentifier)
string departmentIdentifier, string departmentSapId)
{
// Plasser denne en annen plass
// FIXME:Plasser denne en annen plass
var baseUri = "https://fusion-s-portal-ci.azurewebsites.net/apps/personnel-allocation/";
var avdelingsId = "52586050"; // Må finne ut av hvor man får denne ID'en til avdelingen. Mulig man må hente det fra context?


var formatDoubleToHaveOneDecimal = "F1";

var card = new AdaptiveCardBuilder()
.AddHeading($"**Weekly summary - {departmentIdentifier}**")
Expand All @@ -392,11 +362,11 @@ private static AdaptiveCard ResourceOwnerAdaptiveCardBuilder(ResourceOwnerAdapti
.AddColumnSet(new AdaptiveCardColumn(cardData.NumberOfRequestsStartingInLessThanThreeMonths.ToString(), "Requests with start date < 3 months"))
.AddColumnSet(new AdaptiveCardColumn(cardData.NumberOfRequestsStartingInMoreThanThreeMonths.ToString(), "Requests with start date > 3 months"))
.AddColumnSet(new AdaptiveCardColumn(cardData.AverageTimeToHandleRequests.ToString(formatDoubleToHaveOneDecimal), "Average time to handle request", "days"))
.AddColumnSet(new AdaptiveCardColumn(cardData.AllocationChangesAwaitingTaskOwnerAction.ToString(), "Allocation changes awaiting task owner action")) // WIP
.AddColumnSet(new AdaptiveCardColumn(cardData.AllocationChangesAwaitingTaskOwnerAction.ToString(), "Allocation changes awaiting task owner action"))
.AddColumnSet(new AdaptiveCardColumn(cardData.ProjectChangesAffectingNextThreeMonths.ToString(), "Project changes affecting next 3 months"))
.AddListContainer("Allocations ending soon with no future allocation:", cardData.PersonnelPositionsEndingWithNoFutureAllocation, "FullName", "EndingPosition")
.AddListContainer("Personnel with more than 100% workload:", cardData.PersonnelAllocatedMoreThan100Percent, "FullName", "TotalWorkload")
.AddActionButton("Go to Personnel allocation app", $"{baseUri}{avdelingsId}")
.AddActionButton("Go to Personnel allocation app", $"{baseUri}/{departmentSapId}")
.Build();

return card;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ private async Task SendResourceOwnersToQueue(ServiceBusSender sender)
{
AzureUniqueId = resourceOwner.AzureUniqueId,
FullDepartment = resourceOwner.FullDepartment,
Role = NotificationRoleType.ResourceOwner
Role = NotificationRoleType.ResourceOwner,
DepartmentSapId = resourceOwner.DepartmentSapId
});
}
catch (Exception e)
Expand Down

0 comments on commit ba37e11

Please sign in to comment.