Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resource availability widget #23

Merged
merged 2 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 114 additions & 1 deletion app/Widgets/ResourceAvailability.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
namespace App\Widgets;

use Arrilot\Widgets\AbstractWidget;
use Carbon\Carbon;
use App\Models\Resource;
use Illuminate\Support\Facades\Log;

class ResourceAvailability extends AbstractWidget
{
Expand All @@ -19,10 +22,120 @@ class ResourceAvailability extends AbstractWidget
*/
public function run()
{
//
// work out availability
$nextThreeMonths = [];

for ($i = 0; $i < 3; $i++) {
$date = Carbon::now()->addMonthsNoOverflow($i);
$nextThreeMonths[] = [
'year' => $date->year,
'month' => $date->month,
'monthName' => $date->format('F'),
];
}

$resources = Resource::whereHas('contracts', function ($query) {
$query->where('start_date', '<=', now())
->where('end_date', '>=', now());
})->paginate();

foreach ($resources as $resource) {

$resourceAvailability[$resource->id] = [
'name' => $resource->full_name,
];
$currentContract = $resource->contracts()->where('start_date', '<=', now())
->where('end_date', '>=', now())
->first();

if ($currentContract) {
// Calculate base availability for each month
$contractStartDate = Carbon::parse($currentContract->start_date);
$contractEndDate = Carbon::parse($currentContract->end_date);

foreach ($nextThreeMonths as $month) {
$monthStartDate = Carbon::create($month['year'], $month['month'], 1);
$monthEndDate = $monthStartDate->copy()->endOfMonth();

if (
$contractStartDate->isBetween($monthStartDate, $monthEndDate) ||
$contractEndDate->isBetween($monthStartDate, $monthEndDate) ||
($contractStartDate->lessThanOrEqualTo($monthStartDate) && $contractEndDate->greaterThanOrEqualTo($monthEndDate))
) {
// If the contract overlaps with the month, calculate availability
if (
$contractStartDate->isSameMonth($monthStartDate) ||
$contractEndDate->isSameMonth($monthEndDate)
) {
// If the contract start_date or end_date lands in this month, calculate the percentage of the month inside the contract
$daysInMonth = $monthEndDate->diffInDays($monthStartDate) + 1;
$contractDaysInMonth = min($contractEndDate, $monthEndDate)->diffInDays(max($contractStartDate, $monthStartDate)) + 1;
$baseAvailability = round(($contractDaysInMonth / $daysInMonth) * $currentContract->availability, 2);
} else {
// Otherwise, it will be the availability
$baseAvailability = $currentContract->availability;
}
// Use year-month as the key
$key = $month['year'] . '-' . str_pad($month['month'], 2, '0', STR_PAD_LEFT);

// Add the calculated base availability to the resource availability array
$resourceAvailability[$resource->id]['availability'][$key] = $baseAvailability;
}
//now check for leave
foreach ($resource->leaves as $leave) {
$leaveStartDate = Carbon::parse($leave->start_date);
$leaveEndDate = Carbon::parse($leave->end_date);

if (
$leaveStartDate->isBetween($monthStartDate, $monthEndDate) ||
$leaveEndDate->isBetween($monthStartDate, $monthEndDate) ||
($leaveStartDate->lessThanOrEqualTo($monthStartDate) && $leaveEndDate->greaterThanOrEqualTo($monthEndDate))
) {
// If the leave overlaps with the month, calculate availability
if (
$leaveStartDate->isSameMonth($monthStartDate) ||
$leaveEndDate->isSameMonth($monthEndDate)
) {
// If the leave start_date or end_date lands in this month, calculate the percentage of the month inside the leave
$daysInMonth = $monthEndDate->diffInDays($monthStartDate) + 1;
$leaveDaysInMonth = min($leaveEndDate, $monthEndDate)->diffInDays(max($leaveStartDate, $monthStartDate)) + 1;
$leaveAvailability = round(($leaveDaysInMonth / $daysInMonth) * 1.00, 2);
} else {
// Otherwise, it will be 100
$leaveAvailability = 1.00;
}
// Use year-month as the key
$key = $month['year'] . '-' . str_pad($month['month'], 2, '0', STR_PAD_LEFT);

// Add the calculated base availability to the resource availability array
$resourceAvailability[$resource->id]['availability'][$key] = $resourceAvailability[$resource->id]['availability'][$key] - $leaveAvailability;

}
}
}
}

}
Log::info("resourceAvailability: " . json_encode($resourceAvailability));
// Initialize the array with all year-months and 1.00 as default availability
$yearMonthSums = [];

foreach ($resourceAvailability as $resourceId => $resourceInfo) {
foreach ($resourceInfo['availability'] as $yearMonth => $availability) {
$date = Carbon::createFromFormat('Y-m', $yearMonth);
$yearMonthShortName = $date->format('M');

if (!isset($yearMonthSums[$yearMonthShortName])) {
$yearMonthSums[$yearMonthShortName] = 0;
}
$yearMonthSums[$yearMonthShortName] += $availability;
}
}
Log::info("Year-Month: " . json_encode($yearMonthSums));

return view('widgets.resource_availability', [
'config' => $this->config,
'yearMonthSums' => $yearMonthSums,
]);
}
}
2 changes: 1 addition & 1 deletion resources/views/widgets/average_workload.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div>
<div class="bg-secondary">
<div class="row">
<div class="col mt-0">
<h5 class="card-title">Average Workload</h5>
Expand Down
2 changes: 1 addition & 1 deletion resources/views/widgets/demand_funnel.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div>
<div class="bg-secondary">
<style>
.funnel .funnel-level {
background-color: #f8f9fa;
Expand Down
22 changes: 19 additions & 3 deletions resources/views/widgets/resource_availability.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,25 @@
</div>
</div>
</div>
<h1 class="mt-1 mb-3">16</h1>
<div class="row">
@foreach($yearMonthSums as $month => $sum)
<div class="col">
<div style="margin-bottom: 0; padding-bottom: 0" class="row justify-content-center align-items-end">
<div class="col text-center">
<h1 style="margin-bottom: 0; padding-bottom: 0">{{ number_format(round($sum, 1), 1) }}</h1>
</div>
</div>
<div class="row justify-content-center align-items-start">
<div class="col text-center">
{{ $month }}
</div>
</div>
</div>
@endforeach
</div>

<div class="mb-0">
<span class="text-danger">-3.65%</span>
<span class="text-muted">Since last month</span>
<span class="text-danger">{{ number_format((($yearMonthSums[array_key_last($yearMonthSums)] / $yearMonthSums[array_key_first($yearMonthSums)] - 1) * 100), 2) }}%</span>
<span class="text-muted">next month</span>
</div>
</div>
2 changes: 1 addition & 1 deletion resources/views/widgets/skill_set_coverage.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div>
<div class="bg-secondary">
<div class="row">
<div class="col mt-0">
<h5 class="card-title">Skill Set Coverage and Gaps</h5>
Expand Down
2 changes: 1 addition & 1 deletion resources/views/widgets/unrecovered_costs.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div>
<div class="bg-secondary">
<div class="row">
<div class="col mt-0">
<h5 class="card-title">Unrecovered Resource Cost</h5>
Expand Down
2 changes: 1 addition & 1 deletion resources/views/widgets/upcoming_demand.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div>
<div class="bg-secondary">
<div class="row">
<div class="col mt-0">
<h5 class="card-title">Upcoming Demands</h5>
Expand Down