|
| 1 | +module MuleSoft; end |
| 2 | + |
| 3 | +require "./models" |
| 4 | +require "place_calendar" |
| 5 | + |
| 6 | +class MuleSoft::CalendarExporter < PlaceOS::Driver |
| 7 | + descriptive_name "MuleSoft Bookings to Calendar Events Exporter" |
| 8 | + generic_name :Bookings |
| 9 | + description %(Retrieves and creates bookings using the MuleSoft API) |
| 10 | + |
| 11 | + default_settings({ |
| 12 | + calendar_time_zone: "Australia/Sydney" |
| 13 | + }) |
| 14 | + |
| 15 | + accessor calendar : Calendar_1 |
| 16 | + |
| 17 | + @time_zone_string : String | Nil = "Australia/Sydney" |
| 18 | + @time_zone : Time::Location = Time::Location.load("Australia/Sydney") |
| 19 | + @bookings : Array( Hash(String, Int64 | String | Nil) ) = [] of Hash(String, Int64 | String | Nil) |
| 20 | + @existing_events : Array(JSON::Any) = [] of JSON::Any |
| 21 | + # An array of Attendee that has only the system (room) email address. Generally static |
| 22 | + @just_this_system : NamedTuple(email: String, name: String) = {email: "", name: ""} |
| 23 | + |
| 24 | + def on_load |
| 25 | + @just_this_system = { |
| 26 | + "email": system.email.not_nil!, |
| 27 | + "name": system.name |
| 28 | + } |
| 29 | + on_update |
| 30 | + end |
| 31 | + |
| 32 | + def on_update |
| 33 | + subscriptions.clear |
| 34 | + |
| 35 | + @time_zone_string = setting?(String, :calendar_time_zone).presence |
| 36 | + @time_zone = Time::Location.load(@time_zone_string.not_nil!) if @time_zone_string |
| 37 | + self[:timezone] = Time.local.to_s |
| 38 | + |
| 39 | + subscription = system.subscribe(:Bookings_1, :bookings) do |subscription, mulesoft_bookings| |
| 40 | + logger.debug {"DETECTED changed in Mulesoft Bookings.."} |
| 41 | + @bookings = Array( Hash(String, Int64 | String | Nil) ).from_json(mulesoft_bookings) |
| 42 | + logger.debug {"#{@bookings.size} bookings in total"} |
| 43 | + |
| 44 | + update_events |
| 45 | + @bookings.each {|b| export_booking(b)} |
| 46 | + end |
| 47 | + end |
| 48 | + |
| 49 | + def status() |
| 50 | + { |
| 51 | + "bookings": @bookings, |
| 52 | + "events": @existing_events |
| 53 | + } |
| 54 | + end |
| 55 | + |
| 56 | + def update_events |
| 57 | + logger.debug {"FETCHING existing Calendar events..."} |
| 58 | + @existing_events = fetch_events() |
| 59 | + logger.debug {"#{@existing_events.size} events in total"} |
| 60 | + end |
| 61 | + |
| 62 | + protected def fetch_events(past_span : Time::Span = 14.days, future_span : Time::Span = 14.days) |
| 63 | + now = Time.local @time_zone |
| 64 | + from = now - past_span |
| 65 | + til = now + future_span |
| 66 | + |
| 67 | + calendar.list_events( |
| 68 | + calendar_id: system.email.not_nil!, |
| 69 | + period_start: from.to_unix, |
| 70 | + period_end: til.to_unix |
| 71 | + ).get.as_a |
| 72 | + end |
| 73 | + |
| 74 | + |
| 75 | + protected def export_booking(booking : Hash(String, Int64 | String | Nil)) |
| 76 | + # Mulesoft booking titles are often nil. Use the body instead in this case |
| 77 | + booking["title"] = booking["body"] if booking["title"].nil? |
| 78 | + booking["title"] = "#{booking["recurring_master_id"]} #{booking["title"]}" |
| 79 | + logger.debug {"Checking for existing events that match: #{booking}"} |
| 80 | + |
| 81 | + unless event_already_exists?(booking, @existing_events) |
| 82 | + new_event = { |
| 83 | + title: booking["title"], |
| 84 | + event_start: booking["event_start"], |
| 85 | + event_end: booking["event_end"], |
| 86 | + timezone: @time_zone_string, |
| 87 | + description: booking["body"], |
| 88 | + user_id: system.email.not_nil!, |
| 89 | + attendees: [@just_this_system], |
| 90 | + location: system.name.not_nil! |
| 91 | + } |
| 92 | + logger.debug {">>> EXPORTING booking #{new_event}"} |
| 93 | + calendar.create_event(**new_event) |
| 94 | + end |
| 95 | + end |
| 96 | + |
| 97 | + protected def event_already_exists?(new_event : Hash(String, Int64 | String | Nil), existing_events : Array(JSON::Any)) |
| 98 | + existing_events.each do |existing_event| |
| 99 | + return true if events_match?(new_event, existing_event.as_h) |
| 100 | + end |
| 101 | + false |
| 102 | + end |
| 103 | + |
| 104 | + protected def events_match?(event_a : Hash(String, Int64 | String | Nil), event_b : Hash(String, JSON::Any)) |
| 105 | + event_a.select("event_start", "event_end", "title") == event_b.select("event_start", "event_end", "title") |
| 106 | + end |
| 107 | + |
| 108 | + def delete_all_events(past_days : Int32 = 14, future_days : Int32 = 14) |
| 109 | + events = fetch_events(past_span: past_days.days, future_span: future_days.days) |
| 110 | + event_ids = events.map { |e| e["id"]} |
| 111 | + event_ids.each do |event_id| |
| 112 | + calendar.delete_event(calendar_id: system.email.not_nil!, event_id: event_id) |
| 113 | + end |
| 114 | + "Deleted #{event_ids.size} events" |
| 115 | + end |
| 116 | + |
| 117 | +end |
0 commit comments