From 4681de3182adce217dc27a7d2b659fb28dc14e87 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 10 Mar 2021 12:19:38 +0000 Subject: [PATCH 01/26] feat(guest scrape): initial commit --- drivers/place/guest_scrape.cr | 29 +++++++++++++++++++++++++++++ drivers/place/guest_scrape_spec.cr | 3 +++ 2 files changed, 32 insertions(+) create mode 100644 drivers/place/guest_scrape.cr create mode 100644 drivers/place/guest_scrape_spec.cr diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr new file mode 100644 index 00000000000..45fa0c67ca8 --- /dev/null +++ b/drivers/place/guest_scrape.cr @@ -0,0 +1,29 @@ +class Place::GuestScrape < PlaceOS::Driver + descriptive_name "PlaceOS Guest Scrape" + generic_name :GuestScrape + + default_settings({ + zone_ids: ["placeos-zone-id"], + internal_domains: ["PlaceOS.com"], + poll_interval: 5 + }) + + accessor staff_api : StaffAPI_1 + + @zone_ids = [] of String + @internal_domains = [] of String + @poll_interval : Time::Span = 5.minutes + + def on_update + schedule.clear + + @zone_ids = setting?(Array(String), :zone_ids) || [] of String + @internal_domains = setting?(Array(String), :zone_ids) || [] of String + @poll_interval = (setting?(UInt32, :poll_interval) || 5).minutes + end + + def get_bookings + logger.debug { "Getting bookings for zones" } + logger.debug { @zone_ids.inspect } + end +end diff --git a/drivers/place/guest_scrape_spec.cr b/drivers/place/guest_scrape_spec.cr new file mode 100644 index 00000000000..fcd30e546e7 --- /dev/null +++ b/drivers/place/guest_scrape_spec.cr @@ -0,0 +1,3 @@ +DriverSpecs.mock_driver "Place::Bookings" do + exec(:get_bookings) +end From 1750ca1802584e00022be0923f3f87fe347cb091 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Wed, 10 Mar 2021 13:02:38 +0000 Subject: [PATCH 02/26] feat(guest scrape spec): add StaffAPI to spec --- drivers/place/guest_scrape_spec.cr | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/place/guest_scrape_spec.cr b/drivers/place/guest_scrape_spec.cr index fcd30e546e7..461bdc30c7d 100644 --- a/drivers/place/guest_scrape_spec.cr +++ b/drivers/place/guest_scrape_spec.cr @@ -1,3 +1,18 @@ -DriverSpecs.mock_driver "Place::Bookings" do +DriverSpecs.mock_driver "Place::GuestScrape" do + system({ + StaffAPI: {StaffAPI} + }) + exec(:get_bookings) end + +class StaffAPI < DriverSpecs::MockDriver + def zone(zone_id : String) + logger.info { "requesting zone #{zone_id}" } + + { + id: zone_id, + tags: ["level"] + } + end +end From ef510df7600dfa5474e0d35b6665d16152da7f1f Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 10:48:21 +0000 Subject: [PATCH 03/26] feat(guest scrape): try to get systems from zones --- drivers/place/guest_scrape.cr | 7 +++++++ drivers/place/staff_api.cr | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index 45fa0c67ca8..a46d50f07a9 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -25,5 +25,12 @@ class Place::GuestScrape < PlaceOS::Driver def get_bookings logger.debug { "Getting bookings for zones" } logger.debug { @zone_ids.inspect } + + systems = [] of System + @zone_ids.each do |z_id| + staff_api.systems(zone_id: z_id).get.as_a.each do |s| + systems |= System.from_json(s) + end + end end end diff --git a/drivers/place/staff_api.cr b/drivers/place/staff_api.cr index edaa51efb6b..105ea985b17 100644 --- a/drivers/place/staff_api.cr +++ b/drivers/place/staff_api.cr @@ -59,6 +59,26 @@ class Place::StaffAPI < PlaceOS::Driver end end + def systems(q : String? = nil, + limit : Int32 = 1000, + offset : Int32 = 0, + zone_id : String? = nil, + module_id : String? = nil, + features : String? = nil, + capacity : Int32? = nil, + bookable : Bool? = nil) + placeos_client.systems.search( + q: q, + limit: limit, + offset: offset, + zone_id: zone_id, + module_id: module_id, + features: features, + capacity: capacity, + bookable: bookable + ) + end + # Staff details returns the information from AD def staff_details(email : String) response = get("/api/staff/v1/people/#{email}", headers: { From 9554a98925fc287bd0b9f05b88692468d759f2b8 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 11:11:42 +0000 Subject: [PATCH 04/26] feat(guest scrape): use System model --- drivers/place/guest_scrape.cr | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index a46d50f07a9..64aa8652d22 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -1,3 +1,5 @@ +require "placeos" + class Place::GuestScrape < PlaceOS::Driver descriptive_name "PlaceOS Guest Scrape" generic_name :GuestScrape @@ -22,6 +24,8 @@ class Place::GuestScrape < PlaceOS::Driver @poll_interval = (setting?(UInt32, :poll_interval) || 5).minutes end + alias System = PlaceOS::Client::API::Models::System + def get_bookings logger.debug { "Getting bookings for zones" } logger.debug { @zone_ids.inspect } From 983fdb34a94bbe968fab84c4aac95a75f7201c07 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 11:14:06 +0000 Subject: [PATCH 05/26] fix(guest scrape): add System properly --- drivers/place/guest_scrape.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index 64aa8652d22..7f803351186 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -33,7 +33,7 @@ class Place::GuestScrape < PlaceOS::Driver systems = [] of System @zone_ids.each do |z_id| staff_api.systems(zone_id: z_id).get.as_a.each do |s| - systems |= System.from_json(s) + systems |= [System.from_json(s.to_json)] end end end From 50da4b9c6eec9769e0da0297c633d08cfc068482 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 11:17:33 +0000 Subject: [PATCH 06/26] chore(guest scrape): add comment re. array union --- drivers/place/guest_scrape.cr | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index 7f803351186..b3e0530a86b 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -33,6 +33,7 @@ class Place::GuestScrape < PlaceOS::Driver systems = [] of System @zone_ids.each do |z_id| staff_api.systems(zone_id: z_id).get.as_a.each do |s| + # Use array union to prevent dupes incase the same system is in multiple zones systems |= [System.from_json(s.to_json)] end end From de3d651c514167271e7fd3ce9ac26d23db8ec329 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 12:36:29 +0000 Subject: [PATCH 07/26] feat(staff api): add module query methods --- drivers/place/staff_api.cr | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/place/staff_api.cr b/drivers/place/staff_api.cr index 105ea985b17..8bd76513615 100644 --- a/drivers/place/staff_api.cr +++ b/drivers/place/staff_api.cr @@ -183,6 +183,60 @@ class Place::StaffAPI < PlaceOS::Driver ) end + # =================================== + # MODULE INFORMATION + # =================================== + def module(module_id : String) + response = get("/api/engine/v2/modules/#{module_id}", headers: { + "Accept" => "application/json", + "Authorization" => "Bearer #{token}", + }) + + raise "unexpected response for module id #{module_id}: #{response.status_code}\n#{response.body}" unless response.success? + + begin + JSON.parse(response.body) + rescue error + logger.debug { "issue parsing module #{module_id}:\n#{response.body.inspect}" } + raise error + end + end + + def modules_from_system(system_id : String) + response = get("/api/engine/v2/modules?control_system_id=#{system_id}", headers: { + "Accept" => "application/json", + "Authorization" => "Bearer #{token}", + }) + + raise "unexpected response for modules for #{system_id}: #{response.status_code}\n#{response.body}" unless response.success? + + begin + JSON.parse(response.body) + rescue error + logger.debug { "issue getting modules for #{system_id}:\n#{response.body.inspect}" } + raise error + end + end + + # TODO: figure out why these 2 methods don't work + # def module(module_id : String) + # placeos_client.modules.fetch module_id + # end + + # def modules(q : String? = nil, + # limit : Int32 = 20, + # offset : Int32 = 0, + # control_system_id : String? = nil, + # driver_id : String? = nil) + # placeos_client.modules.search( + # q: q, + # limit: limit, + # offset: offset, + # control_system_id: control_system_id, + # driver_id: driver_id + # ) + # end + # =================================== # BOOKINGS ACTIONS # =================================== From e20f84186b96eb5b05c2964063d129e140dd4070 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 12:52:26 +0000 Subject: [PATCH 08/26] feat(guest scrape): don't convert to json --- drivers/place/guest_scrape.cr | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index b3e0530a86b..14ef27f317c 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -24,17 +24,22 @@ class Place::GuestScrape < PlaceOS::Driver @poll_interval = (setting?(UInt32, :poll_interval) || 5).minutes end - alias System = PlaceOS::Client::API::Models::System - def get_bookings logger.debug { "Getting bookings for zones" } logger.debug { @zone_ids.inspect } - systems = [] of System + system_ids = [] of String @zone_ids.each do |z_id| - staff_api.systems(zone_id: z_id).get.as_a.each do |s| + staff_api.systems(zone_id: z_id).get.as_a.each do |sys| # Use array union to prevent dupes incase the same system is in multiple zones - systems |= [System.from_json(s.to_json)] + system_ids |= [sys["id"].as_s] + end + end + + module_ids = [] of String + system_ids.each do |sys_id| + staff_api.modules_from_system(sys_id).get.as_a.each do |mod| + module_ids |= [mod["id"].as_s] if mod["name"] == "Bookings" end end end From 7bba7f11e75dc732b52daab118fa818fd71d8281 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 15:14:47 +0000 Subject: [PATCH 09/26] fix(guest scrape): only get system ids with Booking modules --- drivers/place/guest_scrape.cr | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index 14ef27f317c..c202dd5cc77 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -36,11 +36,10 @@ class Place::GuestScrape < PlaceOS::Driver end end - module_ids = [] of String - system_ids.each do |sys_id| - staff_api.modules_from_system(sys_id).get.as_a.each do |mod| - module_ids |= [mod["id"].as_s] if mod["name"] == "Bookings" - end - end + systems_ids_with_booking_modules = system_ids.select { |sys_id| + staff_api.modules_from_system(sys_id).get.as_a.any? { |mod| + mod["name"] == "Bookings" + } + } end end From d8c798259998692e3249146de7b7ae50cac55684 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 15:21:11 +0000 Subject: [PATCH 10/26] chore(guest scrape): add comments --- drivers/place/guest_scrape.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index c202dd5cc77..df088304ed3 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -28,14 +28,14 @@ class Place::GuestScrape < PlaceOS::Driver logger.debug { "Getting bookings for zones" } logger.debug { @zone_ids.inspect } + # Get all the system ids from the zones in @zone_ids system_ids = [] of String @zone_ids.each do |z_id| - staff_api.systems(zone_id: z_id).get.as_a.each do |sys| - # Use array union to prevent dupes incase the same system is in multiple zones - system_ids |= [sys["id"].as_s] - end + # Use array union to prevent dupes incase the same system is in multiple zones + staff_api.systems(zone_id: z_id).get.as_a.each { |sys| system_ids |= [sys["id"].as_s] } end + # Select only the sytem ids that have a booking module systems_ids_with_booking_modules = system_ids.select { |sys_id| staff_api.modules_from_system(sys_id).get.as_a.any? { |mod| mod["name"] == "Bookings" From 96bac74a540434bcf1a88f786eac640585e3d8a3 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 15:21:30 +0000 Subject: [PATCH 11/26] feat(staff api): add get_module_state method --- drivers/place/staff_api.cr | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/place/staff_api.cr b/drivers/place/staff_api.cr index 8bd76513615..67927cf4d6c 100644 --- a/drivers/place/staff_api.cr +++ b/drivers/place/staff_api.cr @@ -218,6 +218,10 @@ class Place::StaffAPI < PlaceOS::Driver end end + def get_module_state(module_id : String) + placeos_client.modules.state module_id + end + # TODO: figure out why these 2 methods don't work # def module(module_id : String) # placeos_client.modules.fetch module_id From 927be2295c96f0202e7c0c1bb475f9eaa334c4f9 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 15:33:53 +0000 Subject: [PATCH 12/26] feat(staff api): add lookup param for get_module_state --- drivers/place/staff_api.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/place/staff_api.cr b/drivers/place/staff_api.cr index 67927cf4d6c..8206b940866 100644 --- a/drivers/place/staff_api.cr +++ b/drivers/place/staff_api.cr @@ -218,8 +218,8 @@ class Place::StaffAPI < PlaceOS::Driver end end - def get_module_state(module_id : String) - placeos_client.modules.state module_id + def get_module_state(module_id : String, lookup : String? = nil) + placeos_client.modules.state(module_id, lookup) end # TODO: figure out why these 2 methods don't work From 58de43f3f0afacae92eb7b8c056e4e98dc7dafa1 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 15:39:47 +0000 Subject: [PATCH 13/26] feat(guest scrape): get bookings from modules --- drivers/place/guest_scrape.cr | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index df088304ed3..f2988847059 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -36,10 +36,16 @@ class Place::GuestScrape < PlaceOS::Driver end # Select only the sytem ids that have a booking module - systems_ids_with_booking_modules = system_ids.select { |sys_id| - staff_api.modules_from_system(sys_id).get.as_a.any? { |mod| - mod["name"] == "Bookings" - } + booking_module_ids = [] of String + system_ids.each { |sys_id| + # Only look for the first booking module + booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } + booking_module_ids |= [booking_module["id"].as_s] if booking_module + } + + # Get all of the bookings from each booking module + bookings = booking_module_ids.flat_map { |mod_id| + placeos_client.modules.state(mod_id, "bookings") } end end From c00beceeba4c5b457a41609619565ab5d8212924 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 15:42:53 +0000 Subject: [PATCH 14/26] fix(guest scrape): get module bookings properly --- drivers/place/guest_scrape.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index f2988847059..0fec7dacb32 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -45,7 +45,7 @@ class Place::GuestScrape < PlaceOS::Driver # Get all of the bookings from each booking module bookings = booking_module_ids.flat_map { |mod_id| - placeos_client.modules.state(mod_id, "bookings") + staff_api.get_module_state(mod_id, "bookings").get.as_a } end end From 043c66c54b2cf29b54761eac35f78dde8ebeb197 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 16:42:38 +0000 Subject: [PATCH 15/26] feat(guest scrape): basic spec for get_bookings --- drivers/place/guest_scrape.cr | 17 +++++++-- drivers/place/guest_scrape_spec.cr | 60 ++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index 0fec7dacb32..e42dcfed722 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -1,5 +1,3 @@ -require "placeos" - class Place::GuestScrape < PlaceOS::Driver descriptive_name "PlaceOS Guest Scrape" generic_name :GuestScrape @@ -16,6 +14,10 @@ class Place::GuestScrape < PlaceOS::Driver @internal_domains = [] of String @poll_interval : Time::Span = 5.minutes + def on_load + on_update + end + def on_update schedule.clear @@ -34,6 +36,8 @@ class Place::GuestScrape < PlaceOS::Driver # Use array union to prevent dupes incase the same system is in multiple zones staff_api.systems(zone_id: z_id).get.as_a.each { |sys| system_ids |= [sys["id"].as_s] } end + logger.debug { "System ids from zones" } + logger.debug { system_ids.inspect } # Select only the sytem ids that have a booking module booking_module_ids = [] of String @@ -42,10 +46,17 @@ class Place::GuestScrape < PlaceOS::Driver booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } booking_module_ids |= [booking_module["id"].as_s] if booking_module } + logger.debug { "Booking module ids" } + logger.debug { booking_module_ids.inspect } # Get all of the bookings from each booking module bookings = booking_module_ids.flat_map { |mod_id| - staff_api.get_module_state(mod_id, "bookings").get.as_a + logger.debug { "Getting bookings for module #{mod_id}" } + b = staff_api.get_module_state(mod_id, "bookings").get.as_a + logger.debug { b.inspect } + b } + logger.debug { "Bookings" } + logger.debug { bookings.inspect } end end diff --git a/drivers/place/guest_scrape_spec.cr b/drivers/place/guest_scrape_spec.cr index 461bdc30c7d..741c400da2b 100644 --- a/drivers/place/guest_scrape_spec.cr +++ b/drivers/place/guest_scrape_spec.cr @@ -7,12 +7,64 @@ DriverSpecs.mock_driver "Place::GuestScrape" do end class StaffAPI < DriverSpecs::MockDriver - def zone(zone_id : String) + def systems(zone_id : String) logger.info { "requesting zone #{zone_id}" } - { - id: zone_id, - tags: ["level"] + sys_1 = { + id: "sys-1", + zones: ["placeos-zone-id"] } + + if zone_id == "placeos-zone-id" + [sys_1] + else + [ + sys_1, + { + id: "sys-2", + zones: ["zone-1"] + } + ] + end + end + + def modules_from_system(system_id : String) + [ + { + id: "mod-1", + control_system_id: system_id, + name: "Calendar" + }, + { + id: "mod-2", + control_system_id: system_id, + name: "Bookings" + }, + { + id: "mod-3", + control_system_id: system_id, + name: "Bookings" + }, + ] + end + + def get_module_state(module_id : String, lookup : String? = nil) + now = Time.local + start = now.at_beginning_of_day.to_unix + ending = now.at_end_of_day.to_unix + + bookings = [{ + event_start: start, + event_end: ending, + id: "booking-1", + host: "testroom1@booking.demo.acaengine.com", + title: "Test in #{module_id}" + }] + + if lookup == "bookings" + bookings + else + {control_ui: "https://if.panel/to_be_used_for_control", bookings: bookings} + end end end From fb7693a460a0d24a259949e2c6b645b6ed5bb97b Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Thu, 11 Mar 2021 18:30:21 +0000 Subject: [PATCH 16/26] fix(guest scrape): cast module bookings correctly --- drivers/place/guest_scrape.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index e42dcfed722..fa21047a4cf 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -52,11 +52,12 @@ class Place::GuestScrape < PlaceOS::Driver # Get all of the bookings from each booking module bookings = booking_module_ids.flat_map { |mod_id| logger.debug { "Getting bookings for module #{mod_id}" } - b = staff_api.get_module_state(mod_id, "bookings").get.as_a + b = JSON.parse(staff_api.get_module_state(mod_id, "bookings").get.as_s) logger.debug { b.inspect } b } logger.debug { "Bookings" } logger.debug { bookings.inspect } + bookings end end From 5475bb8a5d0bfaec945dcb90745022b881d3d904 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Fri, 12 Mar 2021 12:12:39 +0000 Subject: [PATCH 17/26] fix(guest_scrape): deal with no bookings case --- drivers/place/guest_scrape.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index fa21047a4cf..fc420e7ee16 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -52,7 +52,8 @@ class Place::GuestScrape < PlaceOS::Driver # Get all of the bookings from each booking module bookings = booking_module_ids.flat_map { |mod_id| logger.debug { "Getting bookings for module #{mod_id}" } - b = JSON.parse(staff_api.get_module_state(mod_id, "bookings").get.as_s) + b = staff_api.get_module_state(mod_id).get["bookings"]? + b = b ? JSON.parse(b) : [] of JSON::Any logger.debug { b.inspect } b } From 5a90b79eedc68be77caf49ddc77d0409f56c0393 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Fri, 12 Mar 2021 12:20:45 +0000 Subject: [PATCH 18/26] fix(guest scrape): cast json booking resp --- drivers/place/guest_scrape.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index fc420e7ee16..eac91bd8114 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -53,7 +53,7 @@ class Place::GuestScrape < PlaceOS::Driver bookings = booking_module_ids.flat_map { |mod_id| logger.debug { "Getting bookings for module #{mod_id}" } b = staff_api.get_module_state(mod_id).get["bookings"]? - b = b ? JSON.parse(b) : [] of JSON::Any + b = b ? JSON.parse(b.as_s) : [] of JSON::Any logger.debug { b.inspect } b } From ccd89e4b12b8289c3c2fbb63d6e41d0c5d183b96 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Fri, 12 Mar 2021 15:19:41 +0000 Subject: [PATCH 19/26] feat(guest scrape): use event model --- drivers/place/guest_scrape.cr | 50 ++++++++++++++++++++++-------- drivers/place/guest_scrape_spec.cr | 30 +++++++++--------- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index eac91bd8114..4540d0e2fce 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -1,3 +1,5 @@ +require "place_calendar" + class Place::GuestScrape < PlaceOS::Driver descriptive_name "PlaceOS Guest Scrape" generic_name :GuestScrape @@ -9,6 +11,7 @@ class Place::GuestScrape < PlaceOS::Driver }) accessor staff_api : StaffAPI_1 + accessor mailer : VisitorMailer_1 @zone_ids = [] of String @internal_domains = [] of String @@ -39,26 +42,47 @@ class Place::GuestScrape < PlaceOS::Driver logger.debug { "System ids from zones" } logger.debug { system_ids.inspect } - # Select only the sytem ids that have a booking module - booking_module_ids = [] of String + # Mapping of system_ids to booking_module_ids + booking_module_ids = {} of String => String system_ids.each { |sys_id| # Only look for the first booking module - booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } - booking_module_ids |= [booking_module["id"].as_s] if booking_module + next unless booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } + booking_module_ids[sys_id] = booking_module["id"].as_s } logger.debug { "Booking module ids" } logger.debug { booking_module_ids.inspect } - # Get all of the bookings from each booking module - bookings = booking_module_ids.flat_map { |mod_id| - logger.debug { "Getting bookings for module #{mod_id}" } - b = staff_api.get_module_state(mod_id).get["bookings"]? - b = b ? JSON.parse(b.as_s) : [] of JSON::Any - logger.debug { b.inspect } - b + # Get bookings for each room + bookings_by_room = {} of String => Array(PlaceCalendar::Event) + booking_module_ids.each { |sys_id, mod_id| + next unless bookings = staff_api.get_module_state(mod_id).get["bookings"]? + bookings = JSON.parse(bookings.as_s).as_a.map { |b| PlaceCalendar::Event.from_json(b.to_json) } + logger.debug { bookings.inspect } + bookings_by_room[sys_id] = bookings } logger.debug { "Bookings" } - logger.debug { bookings.inspect } - bookings + logger.debug { bookings_by_room.inspect } + + bookings_by_room + end + + def send_emails + send_qr_emails(get_bookings) + end + + private def send_qr_emails(bookings_by_room) + bookings_by_room.each do |sys_id, bookings| + bookings.each do |b| + b.attendees.each do |a| + mailer.send_visitor_qr_email( + a.email, + a.name, + b.creator, + b.event_start, + sys_id + ) + end + end + end end end diff --git a/drivers/place/guest_scrape_spec.cr b/drivers/place/guest_scrape_spec.cr index 741c400da2b..f0720bdce9a 100644 --- a/drivers/place/guest_scrape_spec.cr +++ b/drivers/place/guest_scrape_spec.cr @@ -1,6 +1,7 @@ DriverSpecs.mock_driver "Place::GuestScrape" do system({ - StaffAPI: {StaffAPI} + StaffAPI: {StaffAPI}, + VisitorMailer: {VisitorMailer} }) exec(:get_bookings) @@ -44,7 +45,7 @@ class StaffAPI < DriverSpecs::MockDriver id: "mod-3", control_system_id: system_id, name: "Bookings" - }, + } ] end @@ -53,18 +54,17 @@ class StaffAPI < DriverSpecs::MockDriver start = now.at_beginning_of_day.to_unix ending = now.at_end_of_day.to_unix - bookings = [{ - event_start: start, - event_end: ending, - id: "booking-1", - host: "testroom1@booking.demo.acaengine.com", - title: "Test in #{module_id}" - }] - - if lookup == "bookings" - bookings - else - {control_ui: "https://if.panel/to_be_used_for_control", bookings: bookings} - end + { + bookings: [{ + event_start: start, + event_end: ending, + id: "booking-1", + host: "testroom1@booking.demo.acaengine.com", + title: "Test in #{module_id}" + }].to_json + } end end + +class VisitorMailer < DriverSpecs::MockDriver +end From 41a3d32fa5b6de70ef1b03531a01c96a42b40ad9 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Fri, 12 Mar 2021 15:40:08 +0000 Subject: [PATCH 20/26] fix(guest_scrape): use correct params for mailer --- drivers/place/guest_scrape.cr | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index 4540d0e2fce..40ac1e647d1 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -66,20 +66,17 @@ class Place::GuestScrape < PlaceOS::Driver bookings_by_room end - def send_emails - send_qr_emails(get_bookings) - end - - private def send_qr_emails(bookings_by_room) - bookings_by_room.each do |sys_id, bookings| + def send_qr_emails + get_bookings.each do |sys_id, bookings| bookings.each do |b| b.attendees.each do |a| mailer.send_visitor_qr_email( - a.email, - a.name, - b.creator, - b.event_start, - sys_id + visitor_email: a.email, + visitor_name: a.name, + host_email: b.creator, + event_id: b.id, + event_start: b.event_start, + system_id: sys_id ) end end From 03746c8c0c72b8af0ea69e0f21e9812f0282dd58 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Fri, 12 Mar 2021 16:51:53 +0000 Subject: [PATCH 21/26] fix(guest scrape): convert event_start to Int --- drivers/place/guest_scrape.cr | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index 40ac1e647d1..f17a9712349 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -60,9 +60,6 @@ class Place::GuestScrape < PlaceOS::Driver logger.debug { bookings.inspect } bookings_by_room[sys_id] = bookings } - logger.debug { "Bookings" } - logger.debug { bookings_by_room.inspect } - bookings_by_room end @@ -70,14 +67,25 @@ class Place::GuestScrape < PlaceOS::Driver get_bookings.each do |sys_id, bookings| bookings.each do |b| b.attendees.each do |a| - mailer.send_visitor_qr_email( + params = { visitor_email: a.email, visitor_name: a.name, host_email: b.creator, event_id: b.id, event_start: b.event_start, system_id: sys_id + } + logger.debug { "Sending email with:" } + logger.debug { params.inspect } + result = mailer.send_visitor_qr_email( + visitor_email: a.email, + visitor_name: a.name, + host_email: b.creator, + event_id: b.id, + event_start: b.event_start.to_unix, + system_id: sys_id ) + logger.debug { "Result = #{result.get}" } end end end From 5be46e7e4e89cae7622456644ecef3828516efe0 Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Fri, 12 Mar 2021 17:04:01 +0000 Subject: [PATCH 22/26] feat(guest scrape): don't send to email to host --- drivers/place/guest_scrape.cr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/guest_scrape.cr index f17a9712349..d4cac250cc8 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/guest_scrape.cr @@ -67,6 +67,9 @@ class Place::GuestScrape < PlaceOS::Driver get_bookings.each do |sys_id, bookings| bookings.each do |b| b.attendees.each do |a| + # TODO: confirm if I can always assume the below + # Don't send to the room since the room is the host of the booking + next if a.email == b.creator params = { visitor_email: a.email, visitor_name: a.name, From 3d94bc1c596b45b2382b2da1888dfb7f0bc697ee Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Mon, 15 Mar 2021 11:10:28 +0000 Subject: [PATCH 23/26] chore(event scrape): rename from Guest to Event --- drivers/place/{guest_scrape.cr => event_scrape.cr} | 4 ++-- drivers/place/{guest_scrape_spec.cr => event_scrape_spec.cr} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename drivers/place/{guest_scrape.cr => event_scrape.cr} (97%) rename drivers/place/{guest_scrape_spec.cr => event_scrape_spec.cr} (100%) diff --git a/drivers/place/guest_scrape.cr b/drivers/place/event_scrape.cr similarity index 97% rename from drivers/place/guest_scrape.cr rename to drivers/place/event_scrape.cr index d4cac250cc8..c46b4a79c05 100644 --- a/drivers/place/guest_scrape.cr +++ b/drivers/place/event_scrape.cr @@ -1,8 +1,8 @@ require "place_calendar" -class Place::GuestScrape < PlaceOS::Driver +class Place::EventScrape < PlaceOS::Driver descriptive_name "PlaceOS Guest Scrape" - generic_name :GuestScrape + generic_name :EventScrape default_settings({ zone_ids: ["placeos-zone-id"], diff --git a/drivers/place/guest_scrape_spec.cr b/drivers/place/event_scrape_spec.cr similarity index 100% rename from drivers/place/guest_scrape_spec.cr rename to drivers/place/event_scrape_spec.cr From 0340904bc5b8a1c4ed5f0ed6e28f4f130beb176f Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Mon, 15 Mar 2021 15:26:07 +0000 Subject: [PATCH 24/26] feat(event scrape): update response structure --- drivers/place/event_scrape.cr | 93 ++++++++++++------------------ drivers/place/event_scrape_spec.cr | 8 +-- 2 files changed, 38 insertions(+), 63 deletions(-) diff --git a/drivers/place/event_scrape.cr b/drivers/place/event_scrape.cr index c46b4a79c05..f53519c93ce 100644 --- a/drivers/place/event_scrape.cr +++ b/drivers/place/event_scrape.cr @@ -1,7 +1,7 @@ require "place_calendar" class Place::EventScrape < PlaceOS::Driver - descriptive_name "PlaceOS Guest Scrape" + descriptive_name "PlaceOS Event Scrape" generic_name :EventScrape default_settings({ @@ -11,12 +11,20 @@ class Place::EventScrape < PlaceOS::Driver }) accessor staff_api : StaffAPI_1 - accessor mailer : VisitorMailer_1 @zone_ids = [] of String @internal_domains = [] of String @poll_interval : Time::Span = 5.minutes + alias Event = PlaceCalendar::Event + + struct SystemWithEvents + include JSON::Serializable + + def initialize(@name : String, @zones : Array(String), @events : Array(Event)) + end + end + def on_load on_update end @@ -25,72 +33,43 @@ class Place::EventScrape < PlaceOS::Driver schedule.clear @zone_ids = setting?(Array(String), :zone_ids) || [] of String - @internal_domains = setting?(Array(String), :zone_ids) || [] of String + @internal_domains = setting?(Array(String), :internal_domains) || [] of String @poll_interval = (setting?(UInt32, :poll_interval) || 5).minutes end def get_bookings + response = { + internal_domains: @internal_domains, + systems: {} of String => SystemWithEvents + } + logger.debug { "Getting bookings for zones" } logger.debug { @zone_ids.inspect } - # Get all the system ids from the zones in @zone_ids - system_ids = [] of String @zone_ids.each do |z_id| - # Use array union to prevent dupes incase the same system is in multiple zones - staff_api.systems(zone_id: z_id).get.as_a.each { |sys| system_ids |= [sys["id"].as_s] } + staff_api.systems(zone_id: z_id).get.as_a.each do |sys| + sys_id = sys["id"].as_s + # In case the same system is in multiple zones + next if response[:systems][sys_id]? + + response[:systems][sys_id] = SystemWithEvents.new( + name: sys["name"].as_s, + zones: Array(String).from_json(sys["zones"].to_json), + events: get_system_bookings(sys_id) + ) + end end - logger.debug { "System ids from zones" } - logger.debug { system_ids.inspect } - - # Mapping of system_ids to booking_module_ids - booking_module_ids = {} of String => String - system_ids.each { |sys_id| - # Only look for the first booking module - next unless booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } - booking_module_ids[sys_id] = booking_module["id"].as_s - } - logger.debug { "Booking module ids" } - logger.debug { booking_module_ids.inspect } - - # Get bookings for each room - bookings_by_room = {} of String => Array(PlaceCalendar::Event) - booking_module_ids.each { |sys_id, mod_id| - next unless bookings = staff_api.get_module_state(mod_id).get["bookings"]? - bookings = JSON.parse(bookings.as_s).as_a.map { |b| PlaceCalendar::Event.from_json(b.to_json) } - logger.debug { bookings.inspect } - bookings_by_room[sys_id] = bookings - } - bookings_by_room + + response end - def send_qr_emails - get_bookings.each do |sys_id, bookings| - bookings.each do |b| - b.attendees.each do |a| - # TODO: confirm if I can always assume the below - # Don't send to the room since the room is the host of the booking - next if a.email == b.creator - params = { - visitor_email: a.email, - visitor_name: a.name, - host_email: b.creator, - event_id: b.id, - event_start: b.event_start, - system_id: sys_id - } - logger.debug { "Sending email with:" } - logger.debug { params.inspect } - result = mailer.send_visitor_qr_email( - visitor_email: a.email, - visitor_name: a.name, - host_email: b.creator, - event_id: b.id, - event_start: b.event_start.to_unix, - system_id: sys_id - ) - logger.debug { "Result = #{result.get}" } - end - end + def get_system_bookings(sys_id : String) : Array(Event) + booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } + # If the system has a booking module with bookings + if booking_module && (bookings = staff_api.get_module_state(booking_module["id"].as_s).get["bookings"]?) + JSON.parse(bookings.as_s).as_a.map { |b| Event.from_json(b.to_json) } + else + [] of Event end end end diff --git a/drivers/place/event_scrape_spec.cr b/drivers/place/event_scrape_spec.cr index f0720bdce9a..0a6d3967b69 100644 --- a/drivers/place/event_scrape_spec.cr +++ b/drivers/place/event_scrape_spec.cr @@ -1,7 +1,6 @@ -DriverSpecs.mock_driver "Place::GuestScrape" do +DriverSpecs.mock_driver "Place::EventScrape" do system({ - StaffAPI: {StaffAPI}, - VisitorMailer: {VisitorMailer} + StaffAPI: {StaffAPI} }) exec(:get_bookings) @@ -65,6 +64,3 @@ class StaffAPI < DriverSpecs::MockDriver } end end - -class VisitorMailer < DriverSpecs::MockDriver -end From 934633c3260bb82849543cc7f1eed00b28ad884f Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Mon, 15 Mar 2021 15:52:55 +0000 Subject: [PATCH 25/26] feat(event scrape): add filtering of events by time --- drivers/place/event_scrape.cr | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/place/event_scrape.cr b/drivers/place/event_scrape.cr index f53519c93ce..2a34c788157 100644 --- a/drivers/place/event_scrape.cr +++ b/drivers/place/event_scrape.cr @@ -46,6 +46,9 @@ class Place::EventScrape < PlaceOS::Driver logger.debug { "Getting bookings for zones" } logger.debug { @zone_ids.inspect } + start_epoch = Time.utc.at_beginning_of_day.to_unix + end_epoch = start_epoch + 86400 # seconds in a day + @zone_ids.each do |z_id| staff_api.systems(zone_id: z_id).get.as_a.each do |sys| sys_id = sys["id"].as_s @@ -55,7 +58,7 @@ class Place::EventScrape < PlaceOS::Driver response[:systems][sys_id] = SystemWithEvents.new( name: sys["name"].as_s, zones: Array(String).from_json(sys["zones"].to_json), - events: get_system_bookings(sys_id) + events: get_system_bookings(sys_id, start_epoch, end_epoch) ) end end @@ -63,11 +66,24 @@ class Place::EventScrape < PlaceOS::Driver response end - def get_system_bookings(sys_id : String) : Array(Event) + def get_system_bookings(sys_id : String, start_epoch : Int64?, end_epoch : Int64?) : Array(Event) booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } # If the system has a booking module with bookings if booking_module && (bookings = staff_api.get_module_state(booking_module["id"].as_s).get["bookings"]?) - JSON.parse(bookings.as_s).as_a.map { |b| Event.from_json(b.to_json) } + bookings = JSON.parse(bookings.as_s).as_a.map { |b| Event.from_json(b.to_json) } + + # If both start_epoch and end_epoch are passed + if start_epoch && end_epoch + # Convert start/end_epoch to Time object as Event.event_start.class == Time + start_time = Time.unix(start_epoch) + end_time = Time.unix(end_epoch) + range = (start_time..end_time) + # Only select bookings within start_epoch and end_epoch + bookings.select! { |b| range.includes?(b.event_start) } + bookings + end + + bookings else [] of Event end From 0e234bc95e7c0af2df76c33458f9b91886b9745f Mon Sep 17 00:00:00 2001 From: Philip Kheav Date: Mon, 15 Mar 2021 17:38:12 +0000 Subject: [PATCH 26/26] fix(event scrape): simplify bookings parsing --- drivers/place/event_scrape.cr | 19 ++++++------------- drivers/place/event_scrape_spec.cr | 2 ++ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/place/event_scrape.cr b/drivers/place/event_scrape.cr index 2a34c788157..957174518bf 100644 --- a/drivers/place/event_scrape.cr +++ b/drivers/place/event_scrape.cr @@ -6,15 +6,13 @@ class Place::EventScrape < PlaceOS::Driver default_settings({ zone_ids: ["placeos-zone-id"], - internal_domains: ["PlaceOS.com"], - poll_interval: 5 + internal_domains: ["PlaceOS.com"] }) accessor staff_api : StaffAPI_1 @zone_ids = [] of String @internal_domains = [] of String - @poll_interval : Time::Span = 5.minutes alias Event = PlaceCalendar::Event @@ -30,24 +28,19 @@ class Place::EventScrape < PlaceOS::Driver end def on_update - schedule.clear - @zone_ids = setting?(Array(String), :zone_ids) || [] of String @internal_domains = setting?(Array(String), :internal_domains) || [] of String - @poll_interval = (setting?(UInt32, :poll_interval) || 5).minutes end - def get_bookings + def todays_bookings response = { internal_domains: @internal_domains, systems: {} of String => SystemWithEvents } - logger.debug { "Getting bookings for zones" } - logger.debug { @zone_ids.inspect } - - start_epoch = Time.utc.at_beginning_of_day.to_unix - end_epoch = start_epoch + 86400 # seconds in a day + now = Time.local + start_epoch = now.at_beginning_of_day.to_unix + end_epoch = now.at_end_of_day.to_unix @zone_ids.each do |z_id| staff_api.systems(zone_id: z_id).get.as_a.each do |sys| @@ -70,7 +63,7 @@ class Place::EventScrape < PlaceOS::Driver booking_module = staff_api.modules_from_system(sys_id).get.as_a.find { |mod| mod["name"] == "Bookings" } # If the system has a booking module with bookings if booking_module && (bookings = staff_api.get_module_state(booking_module["id"].as_s).get["bookings"]?) - bookings = JSON.parse(bookings.as_s).as_a.map { |b| Event.from_json(b.to_json) } + bookings = Array(Event).from_json(bookings.as_s) # If both start_epoch and end_epoch are passed if start_epoch && end_epoch diff --git a/drivers/place/event_scrape_spec.cr b/drivers/place/event_scrape_spec.cr index 0a6d3967b69..c59793aeb34 100644 --- a/drivers/place/event_scrape_spec.cr +++ b/drivers/place/event_scrape_spec.cr @@ -12,6 +12,7 @@ class StaffAPI < DriverSpecs::MockDriver sys_1 = { id: "sys-1", + name: "Room 1", zones: ["placeos-zone-id"] } @@ -22,6 +23,7 @@ class StaffAPI < DriverSpecs::MockDriver sys_1, { id: "sys-2", + name: "Room 2", zones: ["zone-1"] } ]