From e83e935a3d93a321a024d6a7b7171ff25318763c Mon Sep 17 00:00:00 2001 From: kotlanj Date: Tue, 25 Nov 2025 15:06:25 +0100 Subject: [PATCH] added detection & deletion of shifts overlapping time off, added RLCZ maintenance shift planning, added removal of shifts from allshifts when removing a shift, fixed inaccurate debug messages --- shifts.ps1 | 95 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/shifts.ps1 b/shifts.ps1 index 0e70606..589a243 100644 --- a/shifts.ps1 +++ b/shifts.ps1 @@ -57,10 +57,10 @@ function Set-Shift { if($script:noplan -eq $false) { $newShift = New-MgTeamScheduleShift -TeamId $teamID -BodyParameter $params -Headers @{ "MS-APP-ACTS-AS" = $userIdAdmin } - Write-Debug("{0} {1}: setting {2}'s shift - {3} {4}-{5}" -f $dayDate, $dayDate.DayOfWeek, $mail, $shiftName, $StartDate, $EndDate) + Write-Debug("{0} {1}: setting {2}'s shift - {3} {4}-{5}" -f $dayDate.Date, $dayDate.DayOfWeek, $mail, $shiftName, $StartDate, $EndDate) } else { - Write-Debug("{0} {1}: Not setting {2}'s shift - {3} {4}-{5} - running with -noplan" -f $dayDate, $dayDate.DayOfWeek, $mail, $shiftName, $StartDate, $EndDate) + Write-Debug("{0} {1}: Not setting {2}'s shift - {3} {4}-{5} - running with -noplan" -f $dayDate.Date, $dayDate.DayOfWeek, $mail, $shiftName, $StartDate, $EndDate) $newshift = 0 } @@ -71,15 +71,16 @@ function Set-Shift { function Remove-Shift { param( [PSCustomObject]$shift, - [string]$teamID + [string]$teamID, + [string]$removal_email ) if($script:noplan -eq $false) { - Write-Debug("{0} {1}: removing {2}'s shift - {2} {3}-{4}" -f $dayDate, $dayDate.DayOfWeek, $futureds_email, $shift.Notes, $shift.StartDateTime, $shift.EndDateTime) + Write-Debug("{0} {1}: removing {2}'s shift - {3} {4}-{5}" -f $dayDate.Date, $dayDate.DayOfWeek, $removal_email, $shift.SharedShift.Notes, $shift.SharedShift.StartDateTime, $shift.SharedShift.EndDateTime) Remove-MgTeamScheduleShift -ShiftId $shift.id -TeamId $teamID } else { - Write-Debug("{0} {1}: Not removing {2}'s shift - {2} {3}-{4} - running with -noplan" -f $dayDate, $dayDate.DayOfWeek, $futureds_email, $shift.Notes, $shift.StartDateTime, $shift.EndDateTime) + Write-Debug("{0} {1}: Not removing {2}'s shift - {3} {4}-{5} - running with -noplan" -f $dayDate.Date, $dayDate.DayOfWeek, $removal_email, $shift.SharedShift.Notes, $shift.SharedShift.StartDateTime, $shift.SharedShift.EndDateTime) } } @@ -92,7 +93,7 @@ function Invoke-HasTimeOff { foreach ($toff in $timeoff) { if($toff.UserId -eq $UID) { - Write-Debug("{0} {1}: {2} has time off" -f $dayDate, $dayDate.DayOfWeek, $mail) + Write-Debug("{0} {1}: {2} has time off" -f $dayDate.Date, $dayDate.DayOfWeek, $mail) return $true break } @@ -108,7 +109,7 @@ function Invoke-HasShift { foreach ($shift in $shifts) { if($shift.UserId -eq $UID) { - Write-Debug("{0} {1}: {2} has a shift already - {3} {4}-{5}" -f $dayDate, $dayDate.DayOfWeek, $mail, $shift.SharedShift.Notes, $shift.SharedShift.StartDateTime, $shift.SharedShift.EndDateTime) + Write-Debug("{0} {1}: {2} has a shift already - {3} {4}-{5}" -f $dayDate.Date, $dayDate.DayOfWeek, $mail, $shift.SharedShift.Notes, $shift.SharedShift.StartDateTime, $shift.SharedShift.EndDateTime) return $true break } @@ -126,7 +127,7 @@ function Invoke-HasShiftorTimeOff { foreach ($shift in $shifts) { if($shift.UserId -eq $UID) { - Write-Debug("{0} {1}: {2} has a shift already - {3} {4}-{5}" -f $dayDate, $dayDate.DayOfWeek, $mail, $shift.SharedShift.Notes, $shift.SharedShift.StartDateTime, $shift.SharedShift.EndDateTime) + Write-Debug("{0} {1}: {2} has a shift already - {3} {4}-{5}" -f $dayDate.Date, $dayDate.DayOfWeek, $mail, $shift.SharedShift.Notes, $shift.SharedShift.StartDateTime, $shift.SharedShift.EndDateTime) return $true break } @@ -134,7 +135,7 @@ function Invoke-HasShiftorTimeOff { foreach ($toff in $timeoff) { if($toff.UserId -eq $UID) { - Write-Debug("{0} {1}: {2} has time off" -f $dayDate, $dayDate.DayOfWeek, $mail) + Write-Debug("{0} {1}: {2} has time off" -f $dayDate.Date, $dayDate.DayOfWeek, $mail) return $true break } @@ -169,7 +170,6 @@ $allemails = $schedule.PSObject.Properties | ForEach-Object { $_.Value.PSObject.Properties | ForEach-Object { if ($_.Name -in @("ds", "ho", "os", "ns")) { if ($_.Name -eq "os") { - # Collect the email field from objects $_.Value | ForEach-Object { $_.email } } else { @@ -200,6 +200,7 @@ $endSpanDate = Get-Date -Day $startSpanDate.AddDays($daysahead).Day -Month $star $rlcz_mtnc_email = "jiri.kotlan@itego.cz" $rlcz_shift_reduction = -3 #How many hours does the next day shift get reduced by +$rlcz_maintenance_length = 4 # --------------------------- | MAIN SCRIPT | --------------------------- @@ -214,7 +215,7 @@ try { if ($clear) { foreach ($shift in $allshifts) { if($shift.SharedShift.Notes -ne "RLCZ") { - Remove-Shift -shift $shift -teamID $team.Id + Remove-Shift -shift $shift -teamID $team.Id -removal_email "someone" } } return @@ -230,7 +231,7 @@ try { # ---| WEEKEND CHECK |--- if($dayDate.DayOfWeek -in @("Saturday", "Sunday")) { - Write-Debug("{0} {1}: skipping weekend day" -f $dayDate, $dayDate.DayOfWeek) + Write-Debug("{0} {1}: skipping weekend day" -f $dayDate.Date, $dayDate.DayOfWeek) continue } @@ -238,7 +239,7 @@ try { $holiday_response = (Invoke-WebRequest -Uri "https://svatky.steelants.cz/api/$($dayDate.ToString('yyyy-MM-dd'))" -Method GET -Headers $headers_holiday) | ConvertFrom-Json if($holiday_response.isPublicHoliday) { - Write-Debug("{0} {1}: skipping holiday" -f $dayDate, $dayDate.DayOfWeek) + Write-Debug("{0} {1}: skipping holiday" -f $dayDate.Date, $dayDate.DayOfWeek) continue } @@ -264,7 +265,7 @@ try { # ---| TODAY'S SHIFTS AND TIME OFF |--- $shifts_today = [Object[]] $allshifts | Where-Object -Filter { - $_.schedulingGroupId -eq $group.Id -and $_.SharedShift.StartDateTime.ToString("yyyy-MM-dd HH:mm") -ge $dateStart.ToString("yyyy-MM-dd HH:mm") -and $_.SharedShift.EndDateTime.ToString("yyyy-MM-dd HH:mm") -le $dateEnd.ToString("yyyy-MM-dd HH:mm") + $_.schedulingGroupId -eq $group.Id -and $_.SharedShift.StartDateTime.ToString("yyyy-MM-dd HH:mm") -ge $dateStart.ToString("yyyy-MM-dd HH:mm") -and $_.SharedShift.EndDateTime.ToString("yyyy-MM-dd HH:mm") -le $dateEnd.ToString("yyyy-MM-dd HH:mm") } $timeoff_today = [Object[]] $timeoff_all | Where-Object { @@ -276,21 +277,74 @@ try { if ($null -eq $manual_dayshifts) { $ds_shift_inplace = $false - Write-Debug("{0} {1}: day shift not yet set" -f $dayDate, $dayDate.DayOfWeek) + Write-Debug("{0} {1}: day shift not yet set" -f $dayDate.Date, $dayDate.DayOfWeek) } else { $ds_shift_inplace = $true - Write-Debug("{0} {1}: day shift set already" -f $dayDate, $dayDate.DayOfWeek) + Write-Debug("{0} {1}: day shift set already" -f $dayDate.Date, $dayDate.DayOfWeek) } # ---| TEAM MEMBER LOOP |--- foreach($email in $allemails) { $userId = $(Get-MgUser -Filter "UserPrincipalName eq '$email' or proxyAddresses/any(c:c eq 'smtp:$email')").Id + # ---| TIME OFF AND SHIFT OVERLAP CHECK |--- + if(Invoke-HasTimeOff -UID $userId -timeoff $timeoff_today -mail $email) { + if(Invoke-HasShift -UID $userId -shifts $shifts_today -mail $email) { + Write-Debug("{0} {1}: {2} has approved time-off and a shift, checking overlap..." -f $dayDate.Date, $dayDate.DayOfWeek, $email) + + # ---| IDENTIFY SHIFT AND TIME OFF VARIABLES |--- + foreach($shift in $shifts_today) { + if($shift.UserId -eq $userId) { + $overlap_shift = $shift + } + } + foreach($timeoff in $timeoff_today) { + if($timeoff.UserId -eq $userId) { + $overlap_timeoff = $timeoff + } + } + if(($null -eq $overlap_shift) -or ($null -eq $overlap_timeoff)) { + Write-Debug("{0} {1}: {2} couldn't find the specific shift or time off, ignoring..." -f $dayDate.Date, $dayDate.DayOfWeek, $email) + continue + } + + # ---| OVERLAP CHECK |--- + if($overlap_shift.SharedShift.StartDateTime.ToString("yyyy-MM-dd HH:mm") -ge $overlap_timeoff.SharedTimeOff.StartDateTime.ToString("yyyy-MM-dd HH:mm")) { + Write-Debug("{0} {1}: {2} today's shift starts after today's time off starts..." -f $dayDate.Date, $dayDate.DayOfWeek, $email) + if($overlap_shift.SharedShift.StartDateTime.ToString("yyyy-MM-dd HH:mm") -lt $overlap_timeoff.SharedTimeOff.EndDateTime.ToString("yyyy-MM-dd HH:mm")) { + Write-Debug("{0} {1}: {2} today's shift starts before today's time off ends... overlap found" -f $dayDate.Date, $dayDate.DayOfWeek, $email) + Remove-Shift -shift $overlap_shift -teamID $team.Id -removal_email $email + $allshifts = $allshifts | Where-Object { $_.SharedShift.Id -ne $overlap_shift.SharedShift.Id } + } + else { + Write-Debug("{0} {1}: {2} today's shift starts after today's time off ends... no overlap" -f $dayDate.Date, $dayDate.DayOfWeek, $email) + } + } + } + } + + # ---| RLCZ MAINTENANCE PLANNER |--- + if($dayDate.DayOfWeek -eq 'Wednesday') { + if($email -eq $rlcz_mtnc_email) { + $tuesdaynumber = (1..$dayDate.Day | ForEach-Object { + (Get-Date -Year $dayDate.Year -Month $dayDate.Month -Day $_).DayOfWeek + } | Where-Object { $_ -eq 'Tuesday' }).Count + + if($tuesdaynumber -ge 2 -and $tuesdaynumber -le 4) { + Write-Debug("{0} {1}: yesterday was Tuesday number {3}, planning RLCZ maintenance shift" -f $dayDate.Date, $dayDate.DayOfWeek, $email, $tuesdaynumber) + $rlcz_starttime = $dateEnd.AddHours(1) + $rlcz_endtime = $rlcz_starttime.AddHours($rlcz_maintenance_length) + $newshift = Set-Shift -userId $userId -groupID $group.id -shiftName "RLCZ" -StartDate $rlcz_starttime -EndDate $rlcz_endtime -color "yellow" -teamID $team.id -mail $email + $allshifts += [Object[]] $newshift + } + } + } + # ---| DAY SHIFT CHECK |--- if($ds_shift_inplace -eq $false) { if($email -eq $ds_email) { - Write-Debug("{0} {1}: checking {2} for day shift availability..." -f $dayDate, $dayDate.DayOfWeek, $email) + Write-Debug("{0} {1}: checking {2} for day shift availability..." -f $dayDate.Date, $dayDate.DayOfWeek, $email) # ---| IF DAY SHIFT CAN BE ASSIGNED TO THE PROPER MEMEBER |--- if((Invoke-HasShiftorTimeOff -UID $userId -shifts $shifts_today -timeoff $timeoff_today -mail $email) -eq $false) { @@ -312,7 +366,7 @@ try { # ---| DAY SHIFT REPLACEMENT CHECK |--- else { - Write-Debug("{0} {1}: looking ahead for a day shift replacement" -f $dayDate, $dayDate.DayOfWeek) + Write-Debug("{0} {1}: looking ahead for a day shift replacement" -f $dayDate.Date, $dayDate.DayOfWeek) # ---| FUTURE DAYS LOOP |--- for ($daysahead = 1; $daysahead -lt $ds_replace_daysahead; $daysahead++) { @@ -362,7 +416,8 @@ try { foreach ($shift in $shifts_today) { if($shift.UserId -eq $futureds_id -and $shift.SharedShift.Notes -ne "On-site") { # ---| REMOVE THE SHIFT |--- - Remove-Shift -shift $shift -teamID $team.Id + Remove-Shift -shift $shift -teamID $team.Id -removal_email $futureds_email + $allshifts = $allshifts | Where-Object { $_.SharedShift.Id -ne $shift.SharedShift.Id } # ---| HOME-OFFICE DAY SHIFT |--- if($futureds_email -in $ho_emails) { @@ -415,7 +470,7 @@ try { # ---| NO-SHIFT CHECK |--- if($email -in $ns_emails) { - Write-Debug("{0} {1}: {2} has no-shift defined for today" -f $dayDate, $dayDate.DayOfWeek, $email) + Write-Debug("{0} {1}: {2} has no-shift defined for today" -f $dayDate.Date, $dayDate.DayOfWeek, $email) continue }