use chrono::{Duration, Utc, NaiveDateTime}; use heromodels::db::{Collection, Db}; use heromodels::models::User; use heromodels::models::calendar::{AttendanceStatus, Attendee, Calendar, Event, EventStatus}; use heromodels_core::Model; fn main() { // Helper to format i64 timestamps let fmt_time = |ts: i64| -> String { let ndt = NaiveDateTime::from_timestamp_opt(ts, 0) .unwrap_or(NaiveDateTime::from_timestamp_opt(0, 0).unwrap()); chrono::DateTime::::from_utc(ndt, Utc) .format("%Y-%m-%d %H:%M") .to_string() }; let fmt_date = |ts: i64| -> String { let ndt = NaiveDateTime::from_timestamp_opt(ts, 0) .unwrap_or(NaiveDateTime::from_timestamp_opt(0, 0).unwrap()); chrono::DateTime::::from_utc(ndt, Utc) .format("%Y-%m-%d") .to_string() }; // Create a new DB instance, reset before every run let db_path = "/tmp/ourdb_calendar_example"; let db = heromodels::db::hero::OurDB::new(db_path, true).expect("Can create DB"); println!("Hero Models - Calendar Usage Example"); println!("===================================="); // --- Create Users First --- println!("\n--- Creating Users ---"); let user1 = User::new() .username("alice_johnson") .email("alice.johnson@company.com") .full_name("Alice Johnson") .is_active(true) .build(); let user2 = User::new() .username("bob_smith") .email("bob.smith@company.com") .full_name("Bob Smith") .is_active(true) .build(); let user3 = User::new() .username("carol_davis") .email("carol.davis@company.com") .full_name("Carol Davis") .is_active(true) .build(); // Store users in database and get their IDs let user_collection = db.collection::().expect("can open user collection"); let (user1_id, stored_user1) = user_collection.set(&user1).expect("can set user1"); let (user2_id, stored_user2) = user_collection.set(&user2).expect("can set user2"); let (user3_id, stored_user3) = user_collection.set(&user3).expect("can set user3"); println!("Created users:"); println!("- User 1 (ID: {}): {}", user1_id, stored_user1.full_name); println!("- User 2 (ID: {}): {}", user2_id, stored_user2.full_name); println!("- User 3 (ID: {}): {}", user3_id, stored_user3.full_name); // --- Create Attendees (embedded in events, not stored separately) --- println!("\n--- Creating Attendees ---"); let attendee1 = Attendee::new(user1_id).status(AttendanceStatus::Accepted); let attendee2 = Attendee::new(user2_id).status(AttendanceStatus::Tentative); let attendee3 = Attendee::new(user3_id); // Default NoResponse // --- Create Events with Attendees --- println!("\n--- Creating Events with Enhanced Features ---"); let now = Utc::now(); let event1_start = (now + Duration::hours(1)).timestamp(); let event1_end = (now + Duration::hours(2)).timestamp(); let event1 = Event::new() .title("Team Meeting") .reschedule(event1_start, event1_end) .description("Weekly sync-up meeting to discuss project progress.") .location("Conference Room A") .color("#FF5722") // Red-orange color .created_by(user1_id) .status(EventStatus::Published) .category("Work") .reminder_minutes(15) .timezone("UTC") .add_attendee(attendee1.clone()) .add_attendee(attendee2.clone()); let event2_start = (now + Duration::days(1)).timestamp(); let event2_end = (now + Duration::days(1) + Duration::minutes(90)).timestamp(); let event2 = Event::new() .title("Project Brainstorm") .reschedule(event2_start, event2_end) .description("Brainstorming session for new project features.") .location("Innovation Lab") .color("#4CAF50") // Green color .created_by(user2_id) .status(EventStatus::Draft) .category("Planning") .reminder_minutes(30) .is_recurring(true) .add_attendee(attendee1.clone()) .add_attendee(attendee3.clone()); let event3_start = (now + Duration::days(2)).timestamp(); let event3_end = (now + Duration::days(2) + Duration::hours(1)).timestamp(); let event3 = Event::new() .title("Client Call") .reschedule(event3_start, event3_end) .description("Quarterly review with key client.") .color("#9C27B0") // Purple color .created_by(user3_id) .status(EventStatus::Published) .category("Client") .reminder_minutes(60) .add_attendee(attendee2.clone()); // Create an all-day event let event4_start = (now + Duration::days(7)).timestamp(); let event4_end = (now + Duration::days(7) + Duration::hours(24)).timestamp(); let event4 = Event::new() .title("Company Holiday") .reschedule(event4_start, event4_end) .description("National holiday - office closed.") .color("#FFC107") // Amber color .all_day(true) .created_by(user1_id) .status(EventStatus::Published) .category("Holiday"); println!("Created events with enhanced features:"); println!( "- Event 1: '{}' at {} with {} attendees", event1.title, fmt_time(event1.start_time), event1.attendees.len() ); println!( " Location: {}", event1 .location .as_ref() .unwrap_or(&"Not specified".to_string()) ); println!( " Color: {}", event1.color.as_ref().unwrap_or(&"Default".to_string()) ); println!( " Category: {}", event1.category.as_ref().unwrap_or(&"None".to_string()) ); println!(" Status: {:?}", event1.status); println!(" Created by: User ID {}", event1.created_by.unwrap_or(0)); println!( " Reminder: {} minutes before", event1.reminder_minutes.unwrap_or(0) ); println!(" All-day: {}", event1.all_day); println!(" Recurring: {}", event1.is_recurring); println!( " Attendee IDs: {:?}", event1 .attendees .iter() .map(|a| a.contact_id) .collect::>() ); println!( "- Event 2: '{}' at {} with {} attendees", event2.title, fmt_time(event2.start_time), event2.attendees.len() ); println!( " Location: {}", event2 .location .as_ref() .unwrap_or(&"Not specified".to_string()) ); println!( " Color: {}", event2.color.as_ref().unwrap_or(&"Default".to_string()) ); println!( " Category: {}", event2.category.as_ref().unwrap_or(&"None".to_string()) ); println!(" Status: {:?}", event2.status); println!(" Created by: User ID {}", event2.created_by.unwrap_or(0)); println!( " Reminder: {} minutes before", event2.reminder_minutes.unwrap_or(0) ); println!(" All-day: {}", event2.all_day); println!(" Recurring: {}", event2.is_recurring); println!( " Attendee IDs: {:?}", event2 .attendees .iter() .map(|a| a.contact_id) .collect::>() ); println!( "- Event 3: '{}' at {} with {} attendees", event3.title, fmt_time(event3.start_time), event3.attendees.len() ); println!( " Location: {}", event3 .location .as_ref() .unwrap_or(&"Not specified".to_string()) ); println!( " Color: {}", event3.color.as_ref().unwrap_or(&"Default".to_string()) ); println!( " Category: {}", event3.category.as_ref().unwrap_or(&"None".to_string()) ); println!(" Status: {:?}", event3.status); println!(" Created by: User ID {}", event3.created_by.unwrap_or(0)); println!( " Reminder: {} minutes before", event3.reminder_minutes.unwrap_or(0) ); println!(" All-day: {}", event3.all_day); println!(" Recurring: {}", event3.is_recurring); println!( " Attendee IDs: {:?}", event3 .attendees .iter() .map(|a| a.contact_id) .collect::>() ); println!( "- Event 4: '{}' at {} (All-day: {})", event4.title, fmt_date(event4.start_time), event4.all_day ); println!( " Color: {}", event4.color.as_ref().unwrap_or(&"Default".to_string()) ); println!( " Category: {}", event4.category.as_ref().unwrap_or(&"None".to_string()) ); println!(" Status: {:?}", event4.status); println!(" Created by: User ID {}", event4.created_by.unwrap_or(0)); // --- Demonstrate Event Manipulation --- println!("\n--- Demonstrating Event Manipulation ---"); // Reschedule an event let new_start = now + Duration::hours(2); let new_end = now + Duration::hours(3); let mut updated_event1 = event1.clone(); updated_event1 = updated_event1.reschedule(new_start.timestamp(), new_end.timestamp()); println!( "Rescheduled '{}' to {}", updated_event1.title, fmt_time(new_start.timestamp()) ); // Remove an attendee updated_event1 = updated_event1.remove_attendee(user1_id); println!( "Removed attendee {} from '{}'. Remaining attendee IDs: {:?}", user1_id, updated_event1.title, updated_event1 .attendees .iter() .map(|a| a.contact_id) .collect::>() ); // Add a new attendee updated_event1 = updated_event1.add_attendee(attendee3.clone()); println!( "Added attendee {} to '{}'. Current attendee IDs: {:?}", user3_id, updated_event1.title, updated_event1 .attendees .iter() .map(|a| a.contact_id) .collect::>() ); // --- Demonstrate Event Status Changes --- println!("\n--- Demonstrating Event Status Changes ---"); // Change event status from Draft to Published let mut updated_event2 = event2.clone(); updated_event2 = updated_event2.status(EventStatus::Published); println!( "Changed '{}' status from Draft to Published", updated_event2.title ); // Cancel an event let mut cancelled_event = event3.clone(); cancelled_event = cancelled_event.status(EventStatus::Cancelled); println!("Cancelled event: '{}'", cancelled_event.title); // Update event with new features let enhanced_start = (now + Duration::days(5)).timestamp(); let enhanced_end = (now + Duration::days(5) + Duration::hours(2)).timestamp(); let enhanced_event = Event::new() .title("Enhanced Meeting") .reschedule(enhanced_start, enhanced_end) .description("Meeting with all new features demonstrated.") .location("Virtual - Zoom") .color("#673AB7") // Deep purple .created_by(user1_id) .status(EventStatus::Published) .category("Demo") .reminder_minutes(45) .timezone("America/New_York") .is_recurring(true) .add_attendee(attendee1) .add_attendee(attendee2) .add_attendee(attendee3); println!("Created enhanced event with all features:"); println!(" Title: {}", enhanced_event.title); println!(" Status: {:?}", enhanced_event.status); println!(" Category: {}", enhanced_event.category.as_ref().unwrap()); println!(" Color: {}", enhanced_event.color.as_ref().unwrap()); println!(" Timezone: {}", enhanced_event.timezone.as_ref().unwrap()); println!(" Recurring: {}", enhanced_event.is_recurring); println!( " Reminder: {} minutes", enhanced_event.reminder_minutes.unwrap() ); println!(" Attendees: {} people", enhanced_event.attendees.len()); // --- Store Events in Database --- // Now that Event is a proper database model, we need to store events first println!("\n--- Storing Events in Database ---"); let event_collection = db.collection::().expect("can open event collection"); // Store events and get their auto-generated IDs let (event1_id, stored_event1) = event_collection.set(&event1).expect("can set event1"); let (event2_id, stored_event2) = event_collection.set(&event2).expect("can set event2"); let (event3_id, stored_event3) = event_collection.set(&event3).expect("can set event3"); let (event4_id, stored_event4) = event_collection.set(&event4).expect("can set event4"); println!("Stored events in database:"); println!( "- Event ID {}: '{}' (Status: {:?})", event1_id, stored_event1.title, stored_event1.status ); println!( "- Event ID {}: '{}' (Status: {:?})", event2_id, stored_event2.title, stored_event2.status ); println!( "- Event ID {}: '{}' (Status: {:?})", event3_id, stored_event3.title, stored_event3.status ); println!( "- Event ID {}: '{}' (All-day: {})", event4_id, stored_event4.title, stored_event4.all_day ); // --- Create Calendars --- // Now calendars store the actual database IDs of the events println!("\n--- Creating Enhanced Calendars ---"); // Create a work calendar with enhanced features let calendar1 = Calendar::new(None, "Work Calendar") .description("Calendar for all work-related events.") .owner_id(user1_id) .is_public(false) .color("#2196F3") // Blue color .add_event(event1_id as i64) .add_event(event2_id as i64); // Create a personal calendar let calendar2 = Calendar::new(None, "Personal Calendar") .description("Personal events and appointments.") .owner_id(user2_id) .is_public(false) .color("#E91E63") // Pink color .add_event(event3_id as i64); // Create a company-wide calendar let calendar3 = Calendar::new(None, "Company Events") .description("Company-wide events and holidays.") .owner_id(user1_id) .is_public(true) .color("#FF9800") // Orange color .add_event(event4_id as i64); println!("Created calendars with enhanced features:"); println!( "- Calendar 1: '{}' with {} events", calendar1.name, calendar1.events.len() ); println!(" Owner: User ID {}", calendar1.owner_id.unwrap_or(0)); println!(" Public: {}", calendar1.is_public); println!( " Color: {}", calendar1.color.as_ref().unwrap_or(&"Default".to_string()) ); println!(" Events: {:?}", calendar1.events); println!( "- Calendar 2: '{}' with {} events", calendar2.name, calendar2.events.len() ); println!(" Owner: User ID {}", calendar2.owner_id.unwrap_or(0)); println!(" Public: {}", calendar2.is_public); println!( " Color: {}", calendar2.color.as_ref().unwrap_or(&"Default".to_string()) ); println!(" Events: {:?}", calendar2.events); println!( "- Calendar 3: '{}' with {} events", calendar3.name, calendar3.events.len() ); println!(" Owner: User ID {}", calendar3.owner_id.unwrap_or(0)); println!(" Public: {}", calendar3.is_public); println!( " Color: {}", calendar3.color.as_ref().unwrap_or(&"Default".to_string()) ); println!(" Events: {:?}", calendar3.events); // --- Store Calendars in DB --- let cal_collection = db .collection::() .expect("can open calendar collection"); let (_, calendar1) = cal_collection.set(&calendar1).expect("can set calendar1"); let (_, calendar2) = cal_collection.set(&calendar2).expect("can set calendar2"); let (_, calendar3) = cal_collection.set(&calendar3).expect("can set calendar3"); println!( "Created calendar1 (ID: {}): Name - '{}' (Owner: {}, Public: {})", calendar1.get_id(), calendar1.name, calendar1.owner_id.unwrap_or(0), calendar1.is_public ); println!( "Created calendar2 (ID: {}): Name - '{}' (Owner: {}, Public: {})", calendar2.get_id(), calendar2.name, calendar2.owner_id.unwrap_or(0), calendar2.is_public ); println!( "Created calendar3 (ID: {}): Name - '{}' (Owner: {}, Public: {})", calendar3.get_id(), calendar3.name, calendar3.owner_id.unwrap_or(0), calendar3.is_public ); // --- Retrieve a Calendar by ID --- let stored_calendar1_opt = cal_collection .get_by_id(calendar1.get_id()) .expect("can try to load calendar1"); assert!( stored_calendar1_opt.is_some(), "Calendar1 should be found in DB" ); let mut stored_calendar1 = stored_calendar1_opt.unwrap(); println!( "\nRetrieved calendar1 from DB: Name - '{}', Events count: {}", stored_calendar1.name, stored_calendar1.events.len() ); assert_eq!(stored_calendar1.name, "Work Calendar"); assert_eq!(stored_calendar1.events.len(), 2); assert_eq!(stored_calendar1.events[0], event1_id as i64); // Check event ID // --- Modify a Calendar (Add/Remove Events) --- // Since events are just IDs, we can add and remove them easily println!("\n--- Modifying Calendar ---"); // Create and store a new event let ne_start = (now + Duration::days(3)).timestamp(); let ne_end = (now + Duration::days(3) + Duration::minutes(30)).timestamp(); let new_event = Event::new() .title("1-on-1 Meeting") .reschedule(ne_start, ne_end) .description("One-on-one meeting with team member.") .location("Office"); let (new_event_id, _stored_new_event) = event_collection.set(&new_event).expect("can set new event"); println!("Created new event with ID: {}", new_event_id); // Add the new event ID to the calendar stored_calendar1 = stored_calendar1.add_event(new_event_id as i64); assert_eq!(stored_calendar1.events.len(), 3); println!( "Added event ID {} to calendar1. Total events: {}", new_event_id, stored_calendar1.events.len() ); // Remove an event ID from the calendar stored_calendar1 = stored_calendar1.remove_event(event2_id as i64); // Remove "Project Brainstorm" assert_eq!(stored_calendar1.events.len(), 2); println!( "Removed event ID {} from calendar1. Total events: {}", event2_id, stored_calendar1.events.len() ); // --- Store the modified calendar --- let (_, _stored_calendar1) = cal_collection .set(&stored_calendar1) .expect("can set modified calendar1"); // Verify the changes were persisted let re_retrieved_calendar1_opt = cal_collection .get_by_id(calendar1.get_id()) .expect("can try to load modified calendar1"); let re_retrieved_calendar1 = re_retrieved_calendar1_opt.unwrap(); assert_eq!(re_retrieved_calendar1.events.len(), 2); assert!(re_retrieved_calendar1.events.contains(&(event1_id as i64))); // Team Meeting still there assert!( re_retrieved_calendar1 .events .contains(&(new_event_id as i64)) ); // New event added assert!(!re_retrieved_calendar1.events.contains(&(event2_id as i64))); // Project Brainstorm removed println!( "\nModified and re-saved calendar1. Final events: {:?}", re_retrieved_calendar1.events ); // --- Delete a Calendar --- cal_collection .delete_by_id(calendar2.get_id()) .expect("can delete calendar2"); let deleted_calendar2_opt = cal_collection .get_by_id(calendar2.get_id()) .expect("can try to load deleted calendar2"); assert!( deleted_calendar2_opt.is_none(), "Calendar2 should be deleted from DB" ); println!("\nDeleted calendar2 (ID: {}) from DB.", calendar2.get_id()); println!("Calendar model DB Prefix: {}", Calendar::db_prefix()); // --- Demonstrate Event Retrieval --- println!("\n--- Retrieving Events from Database ---"); // Get all events let all_events = event_collection.get_all().expect("can list all events"); println!("All events in database:"); for event in &all_events { println!( "- Event ID: {}, Title: '{}', Start: {}, Attendees: {}", event.get_id(), event.title, fmt_time(event.start_time), event.attendees.len() ); } println!("Total events in DB: {}", all_events.len()); // Retrieve specific events by ID and show attendee details println!("\nRetrieving specific events:"); if let Some(retrieved_event1) = event_collection .get_by_id(event1_id) .expect("can try to get event1") { println!( "Retrieved Event 1: '{}' with {} attendees", retrieved_event1.title, retrieved_event1.attendees.len() ); // Look up attendee details directly from embedded attendees for attendee in &retrieved_event1.attendees { if let Some(user) = user_collection .get_by_id(attendee.contact_id) .expect("can try to get user") { println!( " - User {}: {} (Status: {:?})", attendee.contact_id, user.full_name, attendee.status ); } } } // --- List All Calendars --- println!("\n--- Listing All Enhanced Calendars ---"); let all_calendars = cal_collection.get_all().expect("can list all calendars"); for calendar in &all_calendars { println!( "- Calendar ID: {}, Name: '{}', Owner: {}, Public: {}, Color: {}", calendar.get_id(), calendar.name, calendar.owner_id.unwrap_or(0), calendar.is_public, calendar.color.as_ref().unwrap_or(&"Default".to_string()) ); println!( " Description: {}", calendar.description.as_ref().unwrap_or(&"None".to_string()) ); println!(" Events: {:?}", calendar.events); // Show which events are in this calendar with their details for &event_id in &calendar.events { if let Some(event) = event_collection .get_by_id(event_id as u32) .expect("can try to get event") { println!( " * Event: '{}' (Status: {:?}, Category: {}, All-day: {})", event.title, event.status, event.category.as_ref().unwrap_or(&"None".to_string()), event.all_day ); } } } println!("Total calendars in DB: {}", all_calendars.len()); println!("\nExample finished. DB stored at {}", db_path); println!( "To clean up, you can manually delete the directory: {}", db_path ); }