diff --git a/app/jobs/calendar_importer/event_resolver.rb b/app/jobs/calendar_importer/event_resolver.rb index 97d9edd44..4037e8dd2 100644 --- a/app/jobs/calendar_importer/event_resolver.rb +++ b/app/jobs/calendar_importer/event_resolver.rb @@ -210,7 +210,7 @@ def save_all_occurences event_time[:are_spaces_available] = occurence.status if occurence.respond_to?(:status) - unless event.update! data.attributes.merge(event_time) + unless event.update data.attributes.merge(event_time) notices << { event: event, errors: event.errors.full_messages } end diff --git a/app/jobs/calendar_importer/events/ics_event.rb b/app/jobs/calendar_importer/events/ics_event.rb index 67cf5373d..4b01a6f5a 100644 --- a/app/jobs/calendar_importer/events/ics_event.rb +++ b/app/jobs/calendar_importer/events/ics_event.rb @@ -68,24 +68,27 @@ def find_event_link end def event_link_regex - # (http(s)?://)? - will match against https:// or http:// or nothing - # [A-Za-z0-9]+ - will match against alphanumeric strings - # [^\s]+ - grab until we see a whitespace character - # + http = %r{(http(s)?://)?} # - https:// or http:// or nothing + alphanum = %r{[A-Za-z0-9]+} # - alphanumeric strings + subdomain = %r{(#{alphanum}\.)?} # - matches the www. or us04web in the zoom link + suffix = %r{[^\s<"]+} # - matches until we see a whitespace character, + # an angle bracket, or a quote (thanks html) + # We deal with the following strings: # meet.jit.si/foobarbaz # meet.google.com/kbv-byuf-cvq # facebook.com/events/(really long url) # us04web.zoom.us/j/(really long url) # zoom.us/j/(really long url) + # We also deal with strings like + # + #

(event url)

- http = %r{(http(s)?://)?} - alphanum = %r{[A-Za-z0-9]+} links = { - 'jitsi': %r{#{http}meet.jit.si/[^\s]+}, - 'meets': %r{#{http}meet.google.com/[^\s]+}, - 'facebook': %r{#{http}facebook.com/events/[^\s]+}, - 'zoom': %r{#{http}(#{alphanum}\.)?zoom.us/j/[^\s]+} + 'jitsi': %r{#{http}#{subdomain}meet.jit.si/#{suffix}}, + 'meets': %r{#{http}#{subdomain}meet.google.com/#{suffix}}, + 'facebook': %r{#{http}#{subdomain}facebook.com/events/#{suffix}}, + 'zoom': %r{#{http}#{subdomain}zoom.us/j/#{suffix}} } Regexp.union links.values diff --git a/app/jobs/calendar_importer/events/meetup_event.rb b/app/jobs/calendar_importer/events/meetup_event.rb index bdb6e7cf0..31ad1c701 100644 --- a/app/jobs/calendar_importer/events/meetup_event.rb +++ b/app/jobs/calendar_importer/events/meetup_event.rb @@ -27,7 +27,7 @@ def publisher_url end def location - return nil + return nil # TODO: ??? Why are we ignoring the venue for meetup events venue = @event['venue'] if venue diff --git a/test/jobs/calendar_importer/event_resolver_test.rb b/test/jobs/calendar_importer/event_resolver_test.rb index 83d23d716..03e20d24b 100644 --- a/test/jobs/calendar_importer/event_resolver_test.rb +++ b/test/jobs/calendar_importer/event_resolver_test.rb @@ -1,31 +1,81 @@ require 'test_helper' class EventsResolverTest < ActiveSupport::TestCase - FakeEvent = Struct.new( + FakeICSEvent = Struct.new( :uid, :summary, :description, :location, :rrule, :last_modified, - :ocurrences_between + :ocurrences_between, + :custom_properties, + # Fixes bug where entire argument to .new ended up under the uid value lmao + # Who knew our importer was *that* robust?! + keyword_init: true + ) + + FakeEventbriteEvent = Struct.new( + :id, + :name, + :description, + :venue, + :start, + :end, + :online_event, + :url, + keyword_init: true + ) + + FakeMeetupEvent = Struct.new( + :id, + :name, + :description, + :link, + :venue, + :time, + :utc_offset, + :duration, + :is_online_event, + keyword_init: true ) setup do - @start_date = Date.new(1990, 1, 1) - @end_date = Date.new(1990, 1, 2) + @start_date = DateTime.new(1990, 1, 1, 10, 30) + @end_date = DateTime.new(1990, 1, 2, 11, 40) - fake_event = FakeEvent.new( + @fake_ics_event = FakeICSEvent.new( uid: 123, summary: 'A summary', description: 'A description', location: 'A location', rrule: '', last_modified: '', - ocurrences_between: [[@start_date, @end_date]] + ocurrences_between: [[@start_date, @end_date]], + custom_properties: {} ) - @event_data = CalendarImporter::Events::IcsEvent.new(fake_event, @start_date, @end_date) + @fake_eventbrite_event = FakeEventbriteEvent.new( + id: '111111111111', + name: { 'text': 'A summary' }, + description: { 'text': 'A description' }, + venue: nil, + start: { 'local': @start_date.iso8601 }, + 'end': { 'local': @end_date.iso8601 }, + online_event: true + ) + + @fake_meetup_event = FakeMeetupEvent.new( + id: '111111111', + name: { 'text': 'A summary' }, + description: { 'text': '

This is a meetup description!

' }, + venue: nil, + time: @start_date.to_i, + utc_offset: (@end_date.to_i - @start_date.to_i), + is_online_event: true + ) + + @ics_event_data = CalendarImporter::Events::IcsEvent.new(@fake_ics_event, @start_date, @end_date) end test 'online only strategy' do @@ -33,7 +83,7 @@ class EventsResolverTest < ActiveSupport::TestCase notices = [] from_date = @start_date - resolver = CalendarImporter::EventResolver.new(@event_data, calendar, notices, from_date) + resolver = CalendarImporter::EventResolver.new(@ics_event_data, calendar, notices, from_date) resolver.determine_location_for_strategy # these are not set even if present in source @@ -49,7 +99,7 @@ class EventsResolverTest < ActiveSupport::TestCase notices = [] from_date = @start_date - resolver = CalendarImporter::EventResolver.new(@event_data, calendar, notices, from_date) + resolver = CalendarImporter::EventResolver.new(@ics_event_data, calendar, notices, from_date) resolver.determine_location_for_strategy # these are not set even if present in source @@ -59,4 +109,116 @@ class EventsResolverTest < ActiveSupport::TestCase # still sets partner assert_equal calendar.partner_id, resolver.data.partner_id end + + test 'ics: can detect google meet url in custom properties' do + meet_link = 'https://meet.google.com/aaa-aaaa-aaa' + @fake_ics_event[:custom_properties] = { 'x_google_conference' => [meet_link] } + ics_event_data = CalendarImporter::Events::IcsEvent.new(@fake_ics_event, @start_date, @end_date) + + calendar = create(:calendar, strategy: 'event') # strategy doesn't matter TBH + + resolver = CalendarImporter::EventResolver.new(ics_event_data, calendar, [], @start_date) + resolver.determine_online_location + + assert resolver.data.online_address_id.present? + + online_address = OnlineAddress.find(resolver.data.online_address_id) + assert_equal online_address.url, @fake_ics_event['custom_properties']['x_google_conference'].first + end + + test 'ics: can detect jitsi link in description' do + jitsi_link = 'https://meet.jit.si/blahblabladsf' + @fake_ics_event[:description] = "Join us on jitsi: #{jitsi_link} words words words" + ics_event_data = CalendarImporter::Events::IcsEvent.new(@fake_ics_event, @start_date, @end_date) + + calendar = create(:calendar, strategy: 'event') + + resolver = CalendarImporter::EventResolver.new(ics_event_data, calendar, [], @start_date) + resolver.determine_online_location + + assert resolver.data.online_address_id.present? + + online_address = OnlineAddress.find(resolver.data.online_address_id) + assert_equal online_address.url, jitsi_link + end + + test 'ics: can detect google meet link in description' do + meet_link = 'https://meet.google.com/aaa-aaaa-aaa' + @fake_ics_event[:description] = "Join us on meets: #{meet_link} words words words" + ics_event_data = CalendarImporter::Events::IcsEvent.new(@fake_ics_event, @start_date, @end_date) + + calendar = create(:calendar, strategy: 'event') + + resolver = CalendarImporter::EventResolver.new(ics_event_data, calendar, [], @start_date) + resolver.determine_online_location + + assert resolver.data.online_address_id.present? + + online_address = OnlineAddress.find(resolver.data.online_address_id) + assert_equal online_address.url, meet_link + end + + test 'ics: can detect facebook event link in description' do + fb_link = 'https://www.facebook.com/events/349588176081231/' + @fake_ics_event[:description] = "See more info on facebook: #{fb_link} words words words" + ics_event_data = CalendarImporter::Events::IcsEvent.new(@fake_ics_event, @start_date, @end_date) + + calendar = create(:calendar, strategy: 'event') + + resolver = CalendarImporter::EventResolver.new(ics_event_data, calendar, [], @start_date) + resolver.determine_online_location + + assert resolver.data.online_address_id.present? + + online_address = OnlineAddress.find(resolver.data.online_address_id) + assert_equal online_address.url, fb_link + end + + test 'ics: can detect zoom link in description' do + zoom_link = 'https://us04web.zoom.us/j/78434510758?pwd=aILSsYSJRSb_uO87tFjulZuLAA0eXT.1' + @fake_ics_event[:description] = "join us on zoom:

#{zoom_link}

words words words" + ics_event_data = CalendarImporter::Events::IcsEvent.new(@fake_ics_event, @start_date, @end_date) + + calendar = create(:calendar, strategy: 'event') + + resolver = CalendarImporter::EventResolver.new(ics_event_data, calendar, [], @start_date) + resolver.determine_online_location + + assert resolver.data.online_address_id.present? + + online_address = OnlineAddress.find(resolver.data.online_address_id) + assert_equal online_address.url, zoom_link + end + + test 'eventbrite: can detect online event url' do + eventbrite_link = 'https://www.eventbrite.co.uk/e/some-random-event-woo-hoo-111111111111' + @fake_eventbrite_event[:url] = eventbrite_link + event_data = CalendarImporter::Events::EventbriteEvent.new(@fake_eventbrite_event) + + calendar = create(:calendar, strategy: 'event') + + resolver = CalendarImporter::EventResolver.new(event_data, calendar, [], @start_date) + resolver.determine_online_location + + assert resolver.data.online_address_id.present? + + online_address = OnlineAddress.find(resolver.data.online_address_id) + assert_equal online_address.url, eventbrite_link + end + + test 'meetup: can detect online event url' do + meetup_link = 'https://www.meetup.co.uk/e/some-random-event-woo-hoo-111111111111' + @fake_meetup_event[:link] = meetup_link + event_data = CalendarImporter::Events::MeetupEvent.new(@fake_meetup_event) + + calendar = create(:calendar, strategy: 'event') + + resolver = CalendarImporter::EventResolver.new(event_data, calendar, [], @start_date) + resolver.determine_online_location + + assert resolver.data.online_address_id.present? + + online_address = OnlineAddress.find(resolver.data.online_address_id) + assert_equal online_address.url, meetup_link + end end