Outlook Consolidate Availability for CC and FROM addresses into fixed width table

User Availability

VERSION 1.0 CLASS
BEGIN
  MultiUse = -
1  'True
END
Attribute VB_Name = "UserAvailability"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
' Class Module: UserAvailability
Option Explicit

Private pEmail As String
Private pTimeZone As Outlook.TimeZone 'As String
Private pDays As Collection
'Private pWorkdayStart As Date          ''Currently NO WAY to get work day start or end...Guess with busy/free
'Private pWorkdayEnd As Date

'Public Property Get WorkdayStart() As Date
'    WorkdayStart = pWorkdayStart
'End Property
'
'Public Property Let WorkdayStart(Value As Date)
'    pWorkdayStart = Value
'End Property
'
'Public Property Get WorkdayEnd() As Date
'    WorkdayEnd = pWorkdayEnd
'End Property
'
'Public Property Let WorkdayEnd(Value As Date)
'    pWorkdayEnd = Value
'End Property


Public Property Get Email() As String
    Email = pEmail
End Property

Public Property Let Email(Value As String)
    pEmail = Value
End Property

Public Property Get TimeZone() As Outlook.TimeZone
    TimeZone = pTimeZone
End Property

Public Property Let TimeZone(Value As Outlook.TimeZone)
    pTimeZone = Value
End Property

Public Property Get Days() As Collection
    Set Days = pDays
End Property

Public Sub InitializeDays(StartDate As Date, EndDate As Date)
    Dim d As Date
    Set pDays = New Collection
    For d = StartDate To EndDate
        Dim dayAvail As DayAvailability
        Set dayAvail = New DayAvailability
        dayAvail.DateValue = d ' Use DateValue instead of Date
        dayAvail.InitializeSlots
        pDays.Add dayAvail
    Next d
End Sub


Quarter Hour Slots
VERSION 1.0 CLASS
BEGIN
  MultiUse = -
1  'True
END
Attribute VB_Name = "QuarterHourSlot"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
' Class Module: QuarterHourSlot
Private pStatus As String * 1

Public Property Get Status() As String
    Status = pStatus
End Property

Public Property Let Status(Value As String)
    pStatus = Value
End Property

Daily Availability
VERSION 1.0 CLASS
BEGIN
  MultiUse = -
1  'True
END
Attribute VB_Name = "DayAvailability"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
' Class Module: DayAvailability
Option Explicit

Private pDateValue As Date
Private pSlots() As QuarterHourSlot

Public Property Get DateValue() As Date
    DateValue = pDateValue
End Property

Public Property Let DateValue(Value As Date)
    pDateValue = Value
End Property

Public Sub InitializeSlots()
   
Dim i As Integer
   
ReDim pSlots(0 To 95) ' 96 quarter-hour slots in a day
    For i = 0 To 95
       
Set pSlots(i) = New QuarterHourSlot
        pSlots(i).Status =
"*" ' Default status
    Next i
End Sub

Public Property Get Slot(ByVal Index As Integer) As QuarterHourSlot
   
Set Slot = pSlots(Index)
End Property


Main Module otlCalendar

Attribute VB_Name = "otlCalendarv5_Classes_FreeBusy"
Option Explicit
' In a standard module
Type TimeZoneInfo
    Code
As String
    Offset
As Integer
End Type
''-------------------------------------------------------------
Sub AAAA_GetUserAndCC_Availability()
   
Dim OutlookApp As Outlook.Application
   
Dim Namespace As Outlook.Namespace
   
Dim Recipient As Outlook.Recipient
   
Dim StartDate As Date, EndDate As Date
   
Dim CurrentMail As Outlook.MailItem
   
Dim CurrentUser As Outlook.AddressEntry
   
Dim CurrentUserEmail As String
   
Dim AllEmails As String
   
Dim UserEmails() As String
   
Dim UserCount As Integer
   
Dim i As Integer
   
Dim Users As Collection
   
Dim UserTimeZones() As String
   
Dim UserWorkStart() As Date
   
Dim UserWorkEnd() As Date
   
Dim elapsed As Date
    elapsed =
Date + Time
   
   
' Initialize Outlook application
    Set OutlookApp = New Outlook.Application
   
Set Namespace = OutlookApp.GetNamespace("MAPI")

   
' Define the time range to check for availability
    StartDate = Now
    EndDate = DateAdd(
"d", 7, StartDate)

   
' Get the current active email
    On Error Resume Next
   
Set CurrentMail = OutlookApp.ActiveInspector.CurrentItem
   
On Error GoTo 0

   
If CurrentMail Is Nothing Or CurrentMail.cc = "" Then
        MsgBox
"Please open an email with CC's to search for the calendar openings.", vbExclamation
       
Exit Sub
   
End If

   
' Get the current user's email
    Set CurrentUser = OutlookApp.Session.CurrentUser.AddressEntry
    CurrentUserEmail = GetSMTPAddress_From_AddressEntry(CurrentUser)

   
' Get email addresses from the CC field
    Dim otlAddr As Recipient
   
Dim ccRecipients As String
    ccRecipients =
""
   
For Each otlAddr In CurrentMail.Recipients
       
If otlAddr.Type = olCC Then
            ccRecipients = ccRecipients & GetSMTPAddress_From_AddressEntry(otlAddr.AddressEntry) &
";"
       
End If
   
Next otlAddr

   
' Check if there are no CC recipients
    If Len(ccRecipients) = 0 Then
        MsgBox
"No CC recipients found. Please ensure there are CC recipients in the email.", vbExclamation
       
Exit Sub
   
End If

   
' Combine CC recipients and current user's email
    AllEmails = ccRecipients & CurrentUserEmail

   
' Split the combined string into a String array
    UserEmails = Split(AllEmails, ";")
    UserCount = UBound(UserEmails) +
1

   
' Initialize collection for users
    Set Users = New Collection
   
'ReDim UserTimeZones(1 To UserCount)
    'ReDim UserWorkStart(1 To UserCount)
    'ReDim UserWorkEnd(1 To UserCount)

   
' Populate user data
    For i = 1 To UserCount
       
Dim userAvail As UserAvailability
       
Set userAvail = New UserAvailability
       
With userAvail
            .Email = Trim(UserEmails(i -
1))
            .InitializeDays StartDate, EndDate
       
End With
        Users.Add userAvail

       
' Resolve recipient and get free/busy data
        Set Recipient = Namespace.CreateRecipient(userAvail.Email)
       
If Recipient.Resolve Then
           
' Get user working hours and time zone from free/busy information
            ''Call GetUserWorkingHoursFromFreeBusy(Recipient, UserWorkStart(i), UserWorkEnd(i), UserTimeZones(i))
           
           
With userAvail
                .TimeZone = Recipient.Application.TimeZones.CurrentTimeZone
''<<<DEBUG PROBABLY NOT CORRECT...
                '.WorkdayStart = UserWorkStart(i)
                '.WorkdayEnd = UserWorkEnd(i)
            End With
           
           
' Populate user's availability
            Call PopulateUserAvailability(userAvail, Namespace.GetSharedDefaultFolder(Recipient, olFolderCalendar).Items, StartDate, EndDate)
       
Else
            MsgBox
"Outlook does not recognize the name: " & userAvail.Email, vbExclamation
       
End If
   
Next i

   
' Adjust user availability for work hours
    AdjustUsersForWorkHours Users, UserWorkStart, UserWorkEnd

   
' Display the availability matrix
    DisplayUserAvailability Users
   
    Debug.Print ((
Date + Time) - elapsed) * 24 * 60 * 60
End Sub
''-------------------------------------------------------------

Private Sub GetUserWorkingHoursFromFreeBusy(Recipient As Outlook.Recipient, ByRef WorkStart As Date, ByRef WorkEnd As Date, ByRef TimeZone As Outlook.TimeZone)
   
Dim ns As Outlook.Namespace
   
Dim FreeBusyInfo As String
   
Dim i As Integer
   
Dim busySlots As Integer
   
Dim currentHour As Date
   
   
' Default working hours and time zone
    WorkStart = TimeValue("09:00 AM")
    WorkEnd = TimeValue(
"05:00 PM")
    TimeZone = Application.TimeZones.Item(
0)
   
   
' Get the current session's namespace
    Set ns = Application.GetNamespace("MAPI")
   
   
' Get the free/busy information for the recipient
    FreeBusyInfo = Recipient.FreeBusy(Start:=Now, MinPerChar:=60, CompleteFormat:=True)
   
   
' Analyze free/busy info to determine typical working hours
    busySlots = 0
    currentHour = TimeValue(
"12:00 AM")
   
For i = 1 To Len(FreeBusyInfo)
       
If Mid(FreeBusyInfo, i, 1) <> "0" Then
            busySlots = busySlots +
1
           
If busySlots = 1 Then
                WorkStart = currentHour
           
End If
            WorkEnd = currentHour + TimeSerial(
1, 0, 0)
       
End If
        currentHour = currentHour + TimeSerial(
1, 0, 0)
   
Next i
   
' Assuming UTC for simplicity. You might need to adjust this to match your organization's settings.
    TimeZone = Recipient.Application.TimeZones.CurrentTimeZone.name ''"UTC"
End Sub

Private Sub PopulateUserAvailability(user As UserAvailability, CalendarItems As Outlook.Items, ByVal StartDate As Date, ByVal EndDate As Date)
   
Dim CalendarItem As Outlook.AppointmentItem
   
Dim SlotStart As Date, SlotEnd As Date
   
Dim Interval As Date
   
Dim TotalDays As Long
   
Dim DayIndex As Long
   
Dim SlotIndex As Long

    TotalDays = EndDate - StartDate
    Interval =
15 / 1440 ' 15 minutes interval

   
' Mark slots with busy statuses
    For Each CalendarItem In CalendarItems
        SlotStart = CalendarItem.Start
        SlotEnd = CalendarItem.End

       
If SlotStart < StartDate Then SlotStart = StartDate
       
If SlotEnd > EndDate Then SlotEnd = EndDate

       
Do While SlotStart < SlotEnd
            DayIndex = SlotStart - StartDate +
1
            SlotIndex = (SlotStart - Int(SlotStart)) *
24 * 4
           
If DayIndex >= 1 And DayIndex <= TotalDays Then
               
If SlotIndex >= 0 And SlotIndex < 96 Then
                   
If CalendarItem.BusyStatus = olBusy Then
                        user.Days(DayIndex).Slot(SlotIndex).Status =
"U"
                   
ElseIf CalendarItem.BusyStatus = olTentative Then
                        user.Days(DayIndex).Slot(SlotIndex).Status =
"T"
                   
ElseIf CalendarItem.BusyStatus = olOutOfOffice Then
                        user.Days(DayIndex).Slot(SlotIndex).Status =
"O"
                   
ElseIf CalendarItem.BusyStatus = olWorkingElsewhere Then
                        user.Days(DayIndex).Slot(SlotIndex).Status =
"W"
                   
Else
                        user.Days(DayIndex).Slot(SlotIndex).Status =
"*"
                   
End If
               
End If
           
End If
            SlotStart = SlotStart + Interval
       
Loop
   
Next CalendarItem
End Sub

Private Sub AdjustUsersForWorkHours(Users As Collection, ByRef UserWorkStart() As Date, ByRef UserWorkEnd() As Date)
   
Dim EarliestStart As Date
   
Dim LatestEnd As Date
   
Dim user As UserAvailability
   
Dim Day As DayAvailability
   
Dim SlotIndex As Long

    EarliestStart = TimeValue(
"07:00:00")
    LatestEnd = TimeValue(
"18:00:00")

   
' Determine the earliest start and latest end times
    Dim i As Long
   
'For i = 1 To Users.Count
    '    If UserWorkStart(i) < EarliestStart Then EarliestStart = UserWorkStart(i) ''Cannot retrieve user start/end workday info
    '    If UserWorkEnd(i) > LatestEnd Then LatestEnd = UserWorkEnd(i)
    'Next i

   
' Adjust each user's day availability to exclude times outside all users' work hours
    For Each user In Users
       
For Each Day In user.Days
           
Dim StartSlot As Long
           
Dim EndSlot As Long

            StartSlot = (EarliestStart - Int(EarliestStart)) *
24 * 4
            EndSlot = (LatestEnd - Int(LatestEnd)) *
24 * 4

           
For SlotIndex = 0 To 95
                
If SlotIndex < StartSlot Or SlotIndex >= EndSlot Then
                    Day.Slot(SlotIndex).Status =
"x"
               
End If
           
Next SlotIndex
       
Next Day
   
Next user
End Sub
''---------------------------------------------------------------------
Private Sub DisplayUserAvailability(Users As Collection)
   
Dim Output As String
   
Dim user As UserAvailability
   
Dim Day As DayAvailability
   
Dim SlotIndex As Long
   
Dim longest As Integer
   
Dim StartDate As Date
   
Dim TotalDays As Long
   
Dim i As Long
   
Dim TimeZones(0 To 4) As TimeZoneInfo
   
Dim tzIndex As Integer
   
Dim tzOffset As Integer
   
Dim ND As Integer       ' Next day offset
    Dim curTZ As Integer    ''current timezone relative offset till we get the timezones sorted out....

   
' Time zones and their offsets from UTC
    TimeZones(0).Code = "UTC": TimeZones(0).Offset = 0
    TimeZones(
1).Code = "EST": TimeZones(1).Offset = -5
    TimeZones(
2).Code = "CST": TimeZones(2).Offset = -6
    TimeZones(
3).Code = "MST": TimeZones(3).Offset = -7
    TimeZones(
4).Code = "PST": TimeZones(4).Offset = -8

   
' Find the longest email length
    For Each user In Users
       
If Len(user.Email) > longest Then longest = Len(user.Email)
   
Next user

   
If longest < 15 Then longest = 15 ' Ensure minimum length for UTC label

   
' Get the start date and total number of days from the first user (assuming all users have the same date range)
    StartDate = Users(1).Days(1).DateValue
    TotalDays = Users(
1).Days.Count
   
   
Dim strTZ As String, t As String
    strTZ = Application.TimeZones.CurrentTimeZone.name
    t =
Mid(strTZ, 6, 5)
    curTZ =
CInt(TimeValue(t) * 24)
   
If Mid(strTZ, 5, 1) = "-" Then
        curTZ = -curTZ
   
End If

   
' Generate time header for different time zones
    For tzIndex = LBound(TimeZones) To UBound(TimeZones)
        Output = Output & Right(
String(longest, " ") & "TimeOfDay " & TimeZones(tzIndex).Code & ": ", longest + 2)
        tzOffset = TimeZones(tzIndex).Offset
' Adjust for timezone offsets
        If tzOffset - curTZ < 0 Then tzOffset = tzOffset + 24
       
' Loop through 24 hours in a day
        For SlotIndex = tzOffset - curTZ To tzOffset - curTZ + 23       ''one day
            ' Calculate the adjusted hour considering the timezone offset
            Dim adjustedHour As Integer
            adjustedHour = (SlotIndex)
Mod 24
            Output = Output & Format(adjustedHour,
"00") & "-- "
       
Next SlotIndex
        Output = Output & vbCrLf
   
Next tzIndex

   
' Print each user's availability
    For Each user In Users
        Output = Output & Right(
String(longest, " ") & user.Email, longest) & vbCrLf
       
For i = 1 To TotalDays
           
Set Day = user.Days(i)
            Output = Output & Right(
String(longest, " ") & Format(Day.DateValue, "yyyy-mm-dd"), longest) & ": "
           
For SlotIndex = 0 To 95
               
Select Case SlotIndex Mod 4
                   
Case 3  ''last before next hour - add space
                        Output = Output & LCase(Day.Slot(SlotIndex).Status) & " "              ''add space on hour
                    Case 0  ''new hour
                        Output = Output & UCase(Day.Slot(SlotIndex).Status)
                   
Case Else ''any other quarter hour
                        Output = Output & LCase(Day.Slot(SlotIndex).Status)
               
End Select
           
Next SlotIndex
            Output = Output & vbCrLf
       
Next i
   
Next user

    Debug.Print Output
End Sub




Comments

Popular posts from this blog

Revit area plans adding new types and references (Gross and rentable)

Powerpoint countdown and current time in slides VBA

Revit 2019 and up tab colorizer