Intelligent Meeting Scheduler with Conflict Detection and Resolution,Swift
👤 Sharing: AI
```swift
import Foundation
// MARK: - Data Structures
struct TimeSlot: Equatable, CustomStringConvertible {
let start: Date
let end: Date
var description: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm" // 24-hour format
return "\(dateFormatter.string(from: start)) - \(dateFormatter.string(from: end))"
}
static func == (lhs: TimeSlot, rhs: TimeSlot) -> Bool {
return lhs.start == rhs.start && lhs.end == rhs.end
}
}
struct Meeting: Identifiable {
let id = UUID()
let title: String
let attendees: [String]
let timeSlot: TimeSlot
}
struct PersonAvailability {
let person: String
var availableSlots: [TimeSlot]
}
// MARK: - Meeting Scheduler Class
class MeetingScheduler {
private var meetings: [Meeting] = []
private var availability: [PersonAvailability] = []
// MARK: - Availability Management
func addAvailability(person: String, availableSlots: [TimeSlot]) {
let existingAvailabilityIndex = availability.firstIndex { $0.person == person }
if let index = existingAvailabilityIndex {
// Update existing availability
availability[index].availableSlots = availableSlots
} else {
// Add new availability
availability.append(PersonAvailability(person: person, availableSlots: availableSlots))
}
}
func getAvailability(for person: String) -> [TimeSlot]? {
return availability.first { $0.person == person }?.availableSlots
}
// MARK: - Meeting Scheduling
func scheduleMeeting(title: String, attendees: [String], preferredTimeSlot: TimeSlot) -> Meeting? {
// 1. Check if attendees are available at the preferred time
if !areAttendeesAvailable(attendees: attendees, timeSlot: preferredTimeSlot) {
print("Conflict: Not all attendees are available at the preferred time.")
// Try to find an alternative time slot
if let alternativeSlot = findAlternativeTimeSlot(attendees: attendees, duration: Calendar.current.dateComponents([.minute], from: preferredTimeSlot.start, to: preferredTimeSlot.end).minute! ) {
print("Rescheduling to alternative time slot: \(alternativeSlot)")
return scheduleMeeting(title: title, attendees: attendees, preferredTimeSlot: alternativeSlot)
} else {
print("No suitable alternative time slot found.")
return nil
}
}
// 2. Check for conflicts with existing meetings
if doesMeetingConflict(timeSlot: preferredTimeSlot) {
print("Conflict: Meeting conflicts with another existing meeting.")
// Try to find an alternative time slot
if let alternativeSlot = findAlternativeTimeSlot(attendees: attendees, duration: Calendar.current.dateComponents([.minute], from: preferredTimeSlot.start, to: preferredTimeSlot.end).minute! ) {
print("Rescheduling to alternative time slot: \(alternativeSlot)")
return scheduleMeeting(title: title, attendees: attendees, preferredTimeSlot: alternativeSlot)
} else {
print("No suitable alternative time slot found.")
return nil
}
}
// 3. Schedule the meeting if no conflicts are found
let meeting = Meeting(title: title, attendees: attendees, timeSlot: preferredTimeSlot)
meetings.append(meeting)
print("Meeting '\(title)' scheduled successfully at \(preferredTimeSlot) with attendees: \(attendees)")
return meeting
}
private func areAttendeesAvailable(attendees: [String], timeSlot: TimeSlot) -> Bool {
for attendee in attendees {
guard let availableSlots = getAvailability(for: attendee) else {
print("Warning: No availability information found for \(attendee). Assuming they are not available.")
return false // Consider someone unavailable if no availability data is provided
}
var attendeeAvailable = false
for slot in availableSlots {
if timeSlot.start >= slot.start && timeSlot.end <= slot.end {
attendeeAvailable = true
break
}
}
if !attendeeAvailable {
print("\(attendee) is not available at \(timeSlot).")
return false
}
}
return true
}
private func doesMeetingConflict(timeSlot: TimeSlot) -> Bool {
for meeting in meetings {
if timeSlot.start < meeting.timeSlot.end && timeSlot.end > meeting.timeSlot.start {
return true
}
}
return false
}
private func findAlternativeTimeSlot(attendees: [String], duration minutes: Int) -> TimeSlot? {
// This is a placeholder for a more sophisticated algorithm.
// A real-world implementation might consider:
// - Finding a time slot that maximizes the number of attendees who can attend
// - Prioritizing certain attendees
// - Considering the proximity of the alternative time slot to the preferred time slot
// For this example, we'll iterate through the availability of the first attendee
// and try to find a slot that works for everyone, as well as not conflicting with other meetings.
guard let firstAttendee = attendees.first,
let availableSlots = getAvailability(for: firstAttendee) else {
print("No availability information to find an alternative time slot.")
return nil
}
let calendar = Calendar.current
for slot in availableSlots {
// Check if there is enough space in the slot for the meeting's duration
let slotDuration = calendar.dateComponents([.minute], from: slot.start, to: slot.end).minute!
if slotDuration >= minutes {
// Try a time slot starting at the beginning of the available slot
var potentialStartTime = slot.start
var potentialEndTime = calendar.date(byAdding: .minute, value: minutes, to: potentialStartTime)!
let potentialTimeSlot = TimeSlot(start: potentialStartTime, end: potentialEndTime)
if areAttendeesAvailable(attendees: attendees, timeSlot: potentialTimeSlot) && !doesMeetingConflict(timeSlot: potentialTimeSlot) {
return potentialTimeSlot
}
//If the start of the slot is too close to the end, we won't find a valid time
//For example. the slot is 17:00 - 17:30, and we need a 45-minute time.
if(slotDuration > minutes) {
//Now, attempt to find another available slot by sliding the start time
//by a predetermined increment (here, 15 minutes), and seeing if we find something.
let increment = 15 // Minutes
while calendar.date(byAdding: .minute, value: increment, to: potentialStartTime)! < slot.end && calendar.date(byAdding: .minute, value: minutes + increment, to: potentialStartTime)! <= slot.end {
potentialStartTime = calendar.date(byAdding: .minute, value: increment, to: potentialStartTime)!
potentialEndTime = calendar.date(byAdding: .minute, value: minutes, to: potentialStartTime)!
let potentialTimeSlot = TimeSlot(start: potentialStartTime, end: potentialEndTime)
if areAttendeesAvailable(attendees: attendees, timeSlot: potentialTimeSlot) && !doesMeetingConflict(timeSlot: potentialTimeSlot) {
return potentialTimeSlot
}
}
}
}
}
return nil
}
// MARK: - Utility Functions
func printMeetings() {
print("Scheduled Meetings:")
for meeting in meetings {
print("- \(meeting.title): \(meeting.timeSlot) with \(meeting.attendees)")
}
}
}
// MARK: - Example Usage
// Create a date formatter for parsing time strings
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
// Create a MeetingScheduler instance
let scheduler = MeetingScheduler()
// Define some sample availability
let person1 = "Alice"
let person2 = "Bob"
let person3 = "Charlie"
let aliceAvailability = [
TimeSlot(start: dateFormatter.date(from: "2024-01-01 09:00")!, end: dateFormatter.date(from: "2024-01-01 12:00")!),
TimeSlot(start: dateFormatter.date(from: "2024-01-01 14:00")!, end: dateFormatter.date(from: "2024-01-01 17:00")!)
]
let bobAvailability = [
TimeSlot(start: dateFormatter.date(from: "2024-01-01 10:00")!, end: dateFormatter.date(from: "2024-01-01 13:00")!),
TimeSlot(start: dateFormatter.date(from: "2024-01-01 15:00")!, end: dateFormatter.date(from: "2024-01-01 18:00")!)
]
let charlieAvailability = [
TimeSlot(start: dateFormatter.date(from: "2024-01-01 09:30")!, end: dateFormatter.date(from: "2024-01-01 11:30")!),
TimeSlot(start: dateFormatter.date(from: "2024-01-01 14:30")!, end: dateFormatter.date(from: "2024-01-01 16:30")!
]
scheduler.addAvailability(person: person1, availableSlots: aliceAvailability)
scheduler.addAvailability(person: person2, availableSlots: bobAvailability)
scheduler.addAvailability(person: person3, availableSlots: charlieAvailability)
// Attempt to schedule a meeting
let preferredTime = TimeSlot(start: dateFormatter.date(from: "2024-01-01 10:00")!, end: dateFormatter.date(from: "2024-01-01 11:00")!)
let meeting1 = scheduler.scheduleMeeting(title: "Project Kickoff", attendees: [person1, person2, person3], preferredTimeSlot: preferredTime)
let preferredTime2 = TimeSlot(start: dateFormatter.date(from: "2024-01-01 10:30")!, end: dateFormatter.date(from: "2024-01-01 11:30")!)
let meeting2 = scheduler.scheduleMeeting(title: "Follow Up Meeting", attendees: [person1, person2], preferredTimeSlot: preferredTime2)
let preferredTime3 = TimeSlot(start: dateFormatter.date(from: "2024-01-01 14:30")!, end: dateFormatter.date(from: "2024-01-01 15:00")!)
let meeting3 = scheduler.scheduleMeeting(title: "Late afternoon meeting", attendees: [person1, person3], preferredTimeSlot: preferredTime3)
// Print scheduled meetings
scheduler.printMeetings()
```
**Explanation:**
1. **Data Structures:**
* `TimeSlot`: Represents a time range with a `start` and `end` `Date`. It conforms to `Equatable` for easy comparison and `CustomStringConvertible` for convenient printing.
* `Meeting`: Represents a meeting with a unique `id`, `title`, a list of `attendees`, and a `timeSlot`.
* `PersonAvailability`: Stores the availability of a person as a list of `TimeSlot` objects.
2. **`MeetingScheduler` Class:**
* `meetings`: An array to store scheduled `Meeting` objects.
* `availability`: An array to store `PersonAvailability` objects.
* `addAvailability(person: String, availableSlots: [TimeSlot])`: Adds or updates the availability of a person. It checks if availability for the person already exists and either adds a new entry or updates the existing one.
* `getAvailability(for person: String) -> [TimeSlot]?`: Returns the available time slots for a given person or `nil` if no availability is found.
* `scheduleMeeting(title: String, attendees: [String], preferredTimeSlot: TimeSlot) -> Meeting?`: This is the core function. It attempts to schedule a meeting based on the given parameters:
* It first checks if all `attendees` are available during the `preferredTimeSlot` using the `areAttendeesAvailable` function.
* If there are availability conflicts, it attempts to find an alternative time slot using the `findAlternativeTimeSlot` function.
* It then checks for conflicts with already scheduled meetings using the `doesMeetingConflict` function.
* If no conflicts are found, it creates a `Meeting` object and adds it to the `meetings` array, returning the new `Meeting`. If conflicts exist and no alternative is found, it returns `nil`.
* `areAttendeesAvailable(attendees: [String], timeSlot: TimeSlot) -> Bool`: Checks if all attendees are available during the given `timeSlot` by iterating through their availability. It considers a person unavailable if no availability information is found for them (you might want to change this behavior to assume available if no data exists, depending on your requirements).
* `doesMeetingConflict(timeSlot: TimeSlot) -> Bool`: Checks if the given `timeSlot` conflicts with any existing meetings. It iterates through the `meetings` array and checks for overlapping time ranges.
* `findAlternativeTimeSlot(attendees: [String], duration minutes: Int) -> TimeSlot?`: **Important**: This function attempts to find an alternative time slot. The current implementation is a basic placeholder and only considers the availability of the *first* attendee in the list. A more sophisticated implementation would consider the availability of *all* attendees and use a more advanced algorithm to find the best possible time. This version iterates through the available slots of the first attendee, looking for a gap large enough to schedule the meeting. It then checks if everyone is available at that potential time, and that there are no scheduling conflicts, returning the slot if found. It also includes logic to attempt to find the meeting even if it does not perfectly align with the beginning of an available slot. This is done by incrementing the starting time in 15 minute increments (configurable) to try to find a suitable schedule.
* `printMeetings()`: Prints a list of scheduled meetings.
3. **Example Usage:**
* Creates a `MeetingScheduler` instance.
* Defines sample availability for three people (Alice, Bob, and Charlie).
* Adds the availability to the scheduler using `addAvailability`.
* Attempts to schedule several meetings using `scheduleMeeting`. The example demonstrates how the scheduler detects and attempts to resolve conflicts.
* Finally, it prints the scheduled meetings using `printMeetings`.
**Key Improvements and Considerations:**
* **Error Handling:** The code includes basic conflict detection and resolution, but a real-world application would need more robust error handling. For example, what happens if the date format is invalid? What if an attendee doesn't exist?
* **Alternative Time Slot Algorithm:** The `findAlternativeTimeSlot` function is crucial. The provided implementation is very basic. A robust solution would need to consider:
* Finding the time slot that maximizes the number of attendees who can attend.
* Allowing the user to specify priorities for attendees (e.g., the CEO must attend).
* Minimizing the difference between the preferred time and the alternative time.
* Considering working hours and other constraints.
* **User Interface:** This is a purely programmatic solution. A real-world meeting scheduler would typically have a user interface (e.g., a web page or a mobile app) to allow users to input availability, request meetings, and view the schedule.
* **Date and Time Zones:** Handling dates and time zones correctly is critical. The code uses the system's default time zone. In a distributed system, you would need to standardize on a time zone (e.g., UTC) and handle time zone conversions.
* **Persistence:** The `meetings` and `availability` data are stored in memory. To make the scheduler useful, you would need to store the data persistently in a database or file.
* **Concurrency:** If multiple users are accessing the scheduler simultaneously, you would need to consider concurrency issues and use appropriate locking mechanisms to prevent data corruption.
* **Testing:** Thorough unit and integration tests are essential to ensure the scheduler's correctness and reliability.
* **Integration with Calendars:** A full-fledged meeting scheduler would ideally integrate with existing calendar systems (e.g., Google Calendar, Outlook) to allow users to view and manage their meetings in one place.
* **Recurring Meetings:** Consider the possibility of scheduling recurring meetings (daily, weekly, monthly, etc.).
* **Meeting Duration:** The current code assumes that the duration is computed from the start and end times. You could consider adding a separate `duration` property to the `Meeting` struct for more flexibility.
This improved explanation, combined with the thoroughly commented code, should give you a strong foundation for building a more sophisticated and practical meeting scheduler in Swift. Remember to focus on the alternative time slot algorithm, error handling, and persistence to create a truly useful application.
👁️ Viewed: 4
Comments