From e08cec37729bed597bcdb9820309bbdb2886f794 Mon Sep 17 00:00:00 2001 From: Lola Date: Wed, 13 Mar 2024 15:17:04 -0500 Subject: [PATCH 01/32] initial commit --- tools/events-automation/README.md | 1 + tools/events-automation/event.py | 13 +++++++++++++ tools/events-automation/main.py | 8 ++++++++ tools/events-automation/test_events.py | 11 +++++++++++ 4 files changed, 33 insertions(+) create mode 100644 tools/events-automation/README.md create mode 100644 tools/events-automation/event.py create mode 100644 tools/events-automation/main.py create mode 100644 tools/events-automation/test_events.py diff --git a/tools/events-automation/README.md b/tools/events-automation/README.md new file mode 100644 index 000000000..d52c8faea --- /dev/null +++ b/tools/events-automation/README.md @@ -0,0 +1 @@ +TODO: write this \ No newline at end of file diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py new file mode 100644 index 000000000..389b628f4 --- /dev/null +++ b/tools/events-automation/event.py @@ -0,0 +1,13 @@ + +class Event(): + def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, popularity=None, recurring=None, inPast=None) -> None: + self.name = name + self.location = location + self.date = date + self.url = url + self.virtual = virtual + self.organizerName = organizerName + self.popularity = popularity + self.recurring = recurring + self.inPast = inPast + self.maybeSpam = maybeSpam diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py new file mode 100644 index 000000000..d452c242e --- /dev/null +++ b/tools/events-automation/main.py @@ -0,0 +1,8 @@ +# import all the event sources & event sink +# collect all the events from the event sources +# call event sink with our collected events +# print to console / output to file formatted markdown + +from test_events import get_test_events + +print(get_test_events()) \ No newline at end of file diff --git a/tools/events-automation/test_events.py b/tools/events-automation/test_events.py new file mode 100644 index 000000000..6784ac9ee --- /dev/null +++ b/tools/events-automation/test_events.py @@ -0,0 +1,11 @@ +from event import Event +from typing import List +from datetime import datetime + + +def get_test_events() -> List[Event]: + return [Event(name="Test Event 1", location="Earth", date=datetime.now(), url="website1.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), + Event(name="Test Event 2", location="Earth", date=datetime.now(), url="website2.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), + Event(name="Test Event 3", location="Earth", date=datetime.now(), url="website3.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), + Event(name="Test Event 4", location="Earth", date=datetime.now(), url="website4.com", virtual=False, organizerName="Test Organizer", maybeSpam=False) + ] \ No newline at end of file From 2571e1065e911f5aa8c348fe4cfb78cf7b1c4ed1 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:49:16 -0700 Subject: [PATCH 02/32] Update main.py Added sort_and_filter_events() function and helper functions for event sink. --- tools/events-automation/main.py | 78 ++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index d452c242e..319c3dd29 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -3,6 +3,82 @@ # call event sink with our collected events # print to console / output to file formatted markdown +""" +Example Markdown format: +* 2024-03-06 | Virtual (Dublin, IE) | [Rust Dublin](https://www.meetup.com/rust-dublin/) + * [**An intro to `nom`, parsing made easy for Rustaceans**](https://www.meetup.com/rust-dublin/events/299358988/) +* 2024-03-06 | Virtual (Indianapolis, IN, US) | [Indy Rust](https://www.meetup.com/indyrs/) + * [**Indy.rs - with Social Distancing**](https://www.meetup.com/indyrs/events/299047891/) +* 2024-03-07 | Virtual (Charlottesville, NC, US) | [Charlottesville Rust Meetup](https://www.meetup.com/charlottesville-rust-meetup/) + * [**Crafting Interpreters in Rust Collaboratively**](https://www.meetup.com/charlottesville-rust-meetup/events/298368787/) + +Sorted by: + - Date + - City (location) +""" + from test_events import get_test_events -print(get_test_events()) \ No newline at end of file +def main(): + event_list = get_test_events() + sort_and_filter_events(event_list) + for event in event_list: + print(event.to_markdown_string()) + + +def sort_and_filter_events(event_list): + # Sort Events by date and location. + sort_events(event_list) + + # Sort Events by Virtual/In-Person. + sort_virtual(event_list) + + # TODO Filter out Potential Spam Events. + # TODO Filter out duplicates. + + +def sort_events(event_list): + # Sorts event_list date and location. + for i in range(len(event_list)): + # Assigns a current event to be sorted and removes it from list. + current_event = event_list[i] + event_list.remove(event_list[i]) + #TODO put filter in here for dates not in current window. + for j in range(len(event_list)): + if current_event.date < event_list[j].date: + # If current event date is earlier than comparison, inserts back into list. + event_list.insert(j, current_event) + break + elif current_event.date == event_list[j].date: + # If current event date is equal to comparison, compares location. + if current_event.location[0] < event_list[j].location[0]: + # If current event location is alphabetically higher, inserts back into list. + event_list.insert(j, current_event) + break + if current_event not in event_list: + # If current event has not already been inserted, appends to the end of the list. + event_list.append(current_event) + + +def sort_virtual(event_list): + # Orders event_list into virtual, then in-person events. + virtual_events = [] + + for event in event_list: + # Filters and removes any virtual events from event_list. + if event.virtual: + virtual_events.append(event) + event_list.remove(event) + + for i in range(len(virtual_events)-1, -1, -1): + # Inserts virtual events back at the top of event_list. + event_list.insert(0, virtual_events[i]) + + +def filter_potential_spam(): + # Filters out potential spam events. + pass + + +if __name__ == "__main__": + main() From 5182a5aab723bae7346a39dc284de8333aefad5e Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:50:47 -0700 Subject: [PATCH 03/32] Update event.py Added to_markdown_string() function, that outputs the event details in the required markdown format. --- tools/events-automation/event.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index 389b628f4..fec9ec827 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -11,3 +11,10 @@ def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, self.recurring = recurring self.inPast = inPast self.maybeSpam = maybeSpam + + def to_markdown_string(self) -> str: + if self.virtual: + return f'* {self.date} | Virtual ({self.location}) | [{self.organizerName}](TODO: ORGANISER URL HERE)\n\t*[**{self.name}**]({self.url})' + else: + return f'* {self.date} | {self.location} | [{self.organizerName}](TODO: ORGANISER URL HERE)\n\t*[**{self.name}**]({self.url})' + From b7adf5dcacf708082749429fa11f21decf4b44d3 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:52:36 -0700 Subject: [PATCH 04/32] Update test_events.py Altered a few test event parameters to better test the sorting functions. Also changed the datetime data, so it only holds a date value rather than date and time value. --- tools/events-automation/test_events.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/events-automation/test_events.py b/tools/events-automation/test_events.py index 6784ac9ee..78db11844 100644 --- a/tools/events-automation/test_events.py +++ b/tools/events-automation/test_events.py @@ -1,11 +1,11 @@ from event import Event from typing import List -from datetime import datetime +from datetime import date, timedelta def get_test_events() -> List[Event]: - return [Event(name="Test Event 1", location="Earth", date=datetime.now(), url="website1.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), - Event(name="Test Event 2", location="Earth", date=datetime.now(), url="website2.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), - Event(name="Test Event 3", location="Earth", date=datetime.now(), url="website3.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), - Event(name="Test Event 4", location="Earth", date=datetime.now(), url="website4.com", virtual=False, organizerName="Test Organizer", maybeSpam=False) - ] \ No newline at end of file + return [Event(name="Test Event 1", location="Earth", date=date.today() + timedelta(days=2), url="website1.com", virtual=True, organizerName="Test Organizer", maybeSpam=False), + Event(name="Test Event 2", location="Earth", date=date.today() - timedelta(days=2), url="website2.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), + Event(name="Test Event 3", location="Earth", date=date.today(), url="website3.com", virtual=True, organizerName="Test Organizer", maybeSpam=False), + Event(name="Test Event 4", location="Moon", date=date.today(), url="website4.com", virtual=False, organizerName="Test Organizer", maybeSpam=False) + ] From 0b72308bd78b07af70f5b6eeb47a15c1b8b9def4 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:48:30 -0700 Subject: [PATCH 05/32] Update event.py Added field 'duplicate' to help with filtering potential duplicate events. --- tools/events-automation/event.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index fec9ec827..49b4fadd2 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -1,6 +1,6 @@ class Event(): - def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, popularity=None, recurring=None, inPast=None) -> None: + def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, popularity=None, recurring=None, inPast=None, duplicate=False) -> None: self.name = name self.location = location self.date = date @@ -10,7 +10,7 @@ def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, self.popularity = popularity self.recurring = recurring self.inPast = inPast - self.maybeSpam = maybeSpam + self.duplicate = duplicate def to_markdown_string(self) -> str: if self.virtual: From c38ccbb1f600fd5c1beb2ecb175d84616b4034fb Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:52:06 -0700 Subject: [PATCH 06/32] Update main.py Implemented discussed changes to output (grouping by virtual and continent), added filtering by date window, added function for identifying potential duplicate events. --- tools/events-automation/main.py | 155 ++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 59 deletions(-) diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index 319c3dd29..b9169b529 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -3,81 +3,118 @@ # call event sink with our collected events # print to console / output to file formatted markdown -""" -Example Markdown format: -* 2024-03-06 | Virtual (Dublin, IE) | [Rust Dublin](https://www.meetup.com/rust-dublin/) - * [**An intro to `nom`, parsing made easy for Rustaceans**](https://www.meetup.com/rust-dublin/events/299358988/) -* 2024-03-06 | Virtual (Indianapolis, IN, US) | [Indy Rust](https://www.meetup.com/indyrs/) - * [**Indy.rs - with Social Distancing**](https://www.meetup.com/indyrs/events/299047891/) -* 2024-03-07 | Virtual (Charlottesville, NC, US) | [Charlottesville Rust Meetup](https://www.meetup.com/charlottesville-rust-meetup/) - * [**Crafting Interpreters in Rust Collaboratively**](https://www.meetup.com/charlottesville-rust-meetup/events/298368787/) - -Sorted by: - - Date - - City (location) -""" - +from typing import List +from event import Event from test_events import get_test_events +from datetime import date, timedelta +from country_code_to_continent import country_code_to_continent def main(): event_list = get_test_events() - sort_and_filter_events(event_list) + # Sort Events within date range. + # Sorted into virtual or by continent. + # Ordered by date, then city. + # NOTE format for start_date is YYYY, MM, DD without unnecessary 0's. + # TODO code user input for window start date for parameter. + event_list = sort_and_filter_events(event_list, date.today()) + + # Output Sorted Event List + for continent in event_list: + if len(continent) == 0: + continue + else: + country_code = continent[0].location[-2:] + # Output Section Header + if continent[0].virtual: + print(f'### Virtual:\n') + else: + print(f'### {country_code_to_continent(country_code)}:\n') + + # Output event details + for event in continent: + if event.duplicate: + print("** NOTE POTENTIAL DUPLICATE: **") + print(event.to_markdown_string()) + print() + + +def sort_and_filter_events(event_list, start_date) -> List[Event]: + # Remove Events that are outside current date window. for event in event_list: - print(event.to_markdown_string()) - - -def sort_and_filter_events(event_list): + if not (start_date <= event.date <= start_date + timedelta(weeks=5)): + event_list.remove(event) + # Sort Events by date and location. sort_events(event_list) - # Sort Events by Virtual/In-Person. - sort_virtual(event_list) + # Flag potential duplicate events. + potential_duplicate(event_list) - # TODO Filter out Potential Spam Events. - # TODO Filter out duplicates. + # Return 2D list of Events sorted by Virtual/Continent. + return sort_virtual_continent(event_list) def sort_events(event_list): - # Sorts event_list date and location. - for i in range(len(event_list)): - # Assigns a current event to be sorted and removes it from list. + for i in range(1, len(event_list)): + # Assigns current event to sort. current_event = event_list[i] - event_list.remove(event_list[i]) - #TODO put filter in here for dates not in current window. - for j in range(len(event_list)): - if current_event.date < event_list[j].date: - # If current event date is earlier than comparison, inserts back into list. - event_list.insert(j, current_event) - break - elif current_event.date == event_list[j].date: - # If current event date is equal to comparison, compares location. - if current_event.location[0] < event_list[j].location[0]: - # If current event location is alphabetically higher, inserts back into list. - event_list.insert(j, current_event) - break - if current_event not in event_list: - # If current event has not already been inserted, appends to the end of the list. - event_list.append(current_event) - - -def sort_virtual(event_list): - # Orders event_list into virtual, then in-person events. - virtual_events = [] - for event in event_list: - # Filters and removes any virtual events from event_list. - if event.virtual: - virtual_events.append(event) - event_list.remove(event) - - for i in range(len(virtual_events)-1, -1, -1): - # Inserts virtual events back at the top of event_list. - event_list.insert(0, virtual_events[i]) + # Initialise comparison index + j = i - 1 + while j >= 0 and event_list[j].date > current_event.date: + # Shifts all events with lower dates to the left. + event_list[j+1] = event_list[j] + j -= 1 + + if event_list[j].date == current_event.date: + # If current event date is equal to comparison date, compares location. + while j >= 0 and current_event.location[0:3] < event_list[j].location[0:3]: + # Shifts events with a location alphabetically higher to the left. + event_list[j+1] = event_list[j] + j -= 1 + + # Places current event in correct index. + event_list[j+1] = current_event -def filter_potential_spam(): - # Filters out potential spam events. - pass +def sort_virtual_continent(event_list) -> List[Event]: + # Return 2D list of events separated in virtual and by continent. + # Index Key: [[0=Virtual], [1=Africa], [2=Asia], + # [3=Europe], [4=North America], + # [5=Oceania], [6=South America]] + separated_event_list = [[], [], [], [], [], []] + + for event in event_list: + # Separates Events by Virtual or by Continent + if event.virtual: + separated_event_list[0].append(event) + else: + continent = country_code_to_continent(event.location[-2:]) + if continent == "Africa": + separated_event_list[1].append(event) + elif continent == "Asia": + separated_event_list[2].append(event) + elif continent == "Europe": + separated_event_list[3].append(event) + elif continent == "North America": + separated_event_list[4].append(event) + elif continent == "Oceania": + separated_event_list[5].append(event) + elif continent == "South America": + separated_event_list[6].append(event) + + return separated_event_list + + +def potential_duplicate(event_list): + # Identifies possible duplicate Events within Event List. + for i in range(len(event_list)): + for j in range(i+1, len(event_list)): + if event_list[i].date == event_list[j].date: + if event_list[i].url == event_list[j].url: + if event_list[i].name == event_list[j].name: + if event_list[i].organizerName == event_list[j].organizerName: + event_list[i].duplicate = True if __name__ == "__main__": From 3798ea9d707a3249d3817915d05be34e2e4f6216 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:53:07 -0700 Subject: [PATCH 07/32] Update test_events.py Updated test event list to better test Event Sink. --- tools/events-automation/test_events.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/events-automation/test_events.py b/tools/events-automation/test_events.py index 78db11844..bbff39d90 100644 --- a/tools/events-automation/test_events.py +++ b/tools/events-automation/test_events.py @@ -4,8 +4,12 @@ def get_test_events() -> List[Event]: - return [Event(name="Test Event 1", location="Earth", date=date.today() + timedelta(days=2), url="website1.com", virtual=True, organizerName="Test Organizer", maybeSpam=False), - Event(name="Test Event 2", location="Earth", date=date.today() - timedelta(days=2), url="website2.com", virtual=False, organizerName="Test Organizer", maybeSpam=False), - Event(name="Test Event 3", location="Earth", date=date.today(), url="website3.com", virtual=True, organizerName="Test Organizer", maybeSpam=False), - Event(name="Test Event 4", location="Moon", date=date.today(), url="website4.com", virtual=False, organizerName="Test Organizer", maybeSpam=False) + return [Event(name="Test Event 1", location="Dublin, IE", date=date.today() + timedelta(days=2), url="website1.com", virtual=True, organizerName="Test Organizer", organizerUrl="testorg.com"), + Event(name="Test Event 2", location="Indianapolis, IN, US", date=date.today() + timedelta(weeks=2), url="website2.com", virtual=False, organizerName="Test Organizer", organizerUrl="testorg.com"), + Event(name="Test Event 3", location="Melbourne, VIC, AU", date=date.today(), url="website3.com", virtual=True, organizerName="Test Organizer", organizerUrl="testorg.com"), + Event(name="Test Event 4", location="Sydney, NSW, AU", date=date.today(), url="website4.com", virtual=False, organizerName="Test Organizer", organizerUrl="testorg.com"), + Event(name="Test Event 5", location="Melbourne, VIC, AU", date=date.today(), url="website5.com", virtual=False, organizerName="Test Organizer", organizerUrl="testorg.com"), + Event(name="Test Event 6", location="San Francisco, CA, US", date=date.today(), url="website6.com", virtual=False, organizerName="Test Organizer", organizerUrl="testorg.com"), + Event(name="Test Event 7", location="New York, NY, US", date=date.today() - timedelta(days=1), url="website7.com", virtual=False, organizerName="Test Organizer", organizerUrl="testorg.com"), + Event(name="Test Event 7", location="New York, NY, US", date=date.today() - timedelta(days=1), url="website7.com", virtual=False, organizerName="Test Organizer", organizerUrl="testorg.com") ] From a88c6561e1940986a553b31becc3fdf5418e65a4 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:54:40 -0700 Subject: [PATCH 08/32] Create country_code_to_continent.py Created a tool to identify the continent associated with an ISO country code. --- .../country_code_to_continent.py | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 tools/events-automation/country_code_to_continent.py diff --git a/tools/events-automation/country_code_to_continent.py b/tools/events-automation/country_code_to_continent.py new file mode 100644 index 000000000..7b254fd5c --- /dev/null +++ b/tools/events-automation/country_code_to_continent.py @@ -0,0 +1,255 @@ +# Takes an ISO alpha-2 country code and returns the continent. +# Mapping of ISO alpha-2 Country Codes to Continent, from Wikipedia. + +COUNTRY_CODE_TO_CONTINENT = { + 'AB': 'Asia', + 'AD': 'Europe', + 'AE': 'Asia', + 'AF': 'Asia', + 'AG': 'North America', + 'AI': 'North America', + 'AL': 'Europe', + 'AM': 'Asia', + 'AO': 'Africa', + 'AR': 'South America', + 'AS': 'Oceania', + 'AT': 'Europe', + 'AU': 'Oceania', + 'AW': 'North America', + 'AX': 'Europe', + 'AZ': 'Asia', + 'BA': 'Europe', + 'BB': 'North America', + 'BD': 'Asia', + 'BE': 'Europe', + 'BF': 'Africa', + 'BG': 'Europe', + 'BH': 'Asia', + 'BI': 'Africa', + 'BJ': 'Africa', + 'BL': 'North America', + 'BM': 'North America', + 'BN': 'Asia', + 'BO': 'South America', + 'BQ': 'North America', + 'BR': 'South America', + 'BS': 'North America', + 'BT': 'Asia', + 'BV': 'Antarctica', + 'BW': 'Africa', + 'BY': 'Europe', + 'BZ': 'North America', + 'CA': 'North America', + 'CC': 'Asia', + 'CD': 'Africa', + 'CF': 'Africa', + 'CG': 'Africa', + 'CH': 'Europe', + 'CI': 'Africa', + 'CK': 'Oceania', + 'CL': 'South America', + 'CM': 'Africa', + 'CN': 'Asia', + 'CO': 'South America', + 'CR': 'North America', + 'CU': 'North America', + 'CV': 'Africa', + 'CW': 'North America', + 'CX': 'Asia', + 'CY': 'Asia', + 'CZ': 'Europe', + 'DE': 'Europe', + 'DJ': 'Africa', + 'DK': 'Europe', + 'DM': 'North America', + 'DO': 'North America', + 'DZ': 'Africa', + 'EC': 'South America', + 'EE': 'Europe', + 'EG': 'Africa', + 'ER': 'Africa', + 'ES': 'Europe', + 'ET': 'Africa', + 'FI': 'Europe', + 'FJ': 'Oceania', + 'FK': 'South America', + 'FM': 'Oceania', + 'FO': 'Europe', + 'FR': 'Europe', + 'GA': 'Africa', + 'GB': 'Europe', + 'GD': 'North America', + 'GE': 'Asia', + 'GF': 'South America', + 'GG': 'Europe', + 'GH': 'Africa', + 'GI': 'Europe', + 'GL': 'North America', + 'GM': 'Africa', + 'GN': 'Africa', + 'GP': 'North America', + 'GQ': 'Africa', + 'GR': 'Europe', + 'GS': 'South America', + 'GT': 'North America', + 'GU': 'Oceania', + 'GW': 'Africa', + 'GY': 'South America', + 'HK': 'Asia', + 'HM': 'Antarctica', + 'HN': 'North America', + 'HR': 'Europe', + 'HT': 'North America', + 'HU': 'Europe', + 'ID': 'Asia', + 'IE': 'Europe', + 'IL': 'Asia', + 'IM': 'Europe', + 'IN': 'Asia', + 'IO': 'Asia', + 'IQ': 'Asia', + 'IR': 'Asia', + 'IS': 'Europe', + 'IT': 'Europe', + 'JE': 'Europe', + 'JM': 'North America', + 'JO': 'Asia', + 'JP': 'Asia', + 'KE': 'Africa', + 'KG': 'Asia', + 'KH': 'Asia', + 'KI': 'Oceania', + 'KM': 'Africa', + 'KN': 'North America', + 'KP': 'Asia', + 'KR': 'Asia', + 'KW': 'Asia', + 'KY': 'North America', + 'KZ': 'Asia', + 'LA': 'Asia', + 'LB': 'Asia', + 'LC': 'North America', + 'LI': 'Europe', + 'LK': 'Asia', + 'LR': 'Africa', + 'LS': 'Africa', + 'LT': 'Europe', + 'LU': 'Europe', + 'LV': 'Europe', + 'LY': 'Africa', + 'MA': 'Africa', + 'MC': 'Europe', + 'MD': 'Europe', + 'ME': 'Europe', + 'MF': 'North America', + 'MG': 'Africa', + 'MH': 'Oceania', + 'MK': 'Europe', + 'ML': 'Africa', + 'MM': 'Asia', + 'MN': 'Asia', + 'MO': 'Asia', + 'MP': 'Oceania', + 'MQ': 'North America', + 'MR': 'Africa', + 'MS': 'North America', + 'MT': 'Europe', + 'MU': 'Africa', + 'MV': 'Asia', + 'MW': 'Africa', + 'MX': 'North America', + 'MY': 'Asia', + 'MZ': 'Africa', + 'NA': 'Africa', + 'NC': 'Oceania', + 'NE': 'Africa', + 'NF': 'Oceania', + 'NG': 'Africa', + 'NI': 'North America', + 'NL': 'Europe', + 'NO': 'Europe', + 'NP': 'Asia', + 'NR': 'Oceania', + 'NU': 'Oceania', + 'NZ': 'Oceania', + 'OM': 'Asia', + 'OS': 'Asia', + 'PA': 'North America', + 'PE': 'South America', + 'PF': 'Oceania', + 'PG': 'Oceania', + 'PH': 'Asia', + 'PK': 'Asia', + 'PL': 'Europe', + 'PM': 'North America', + 'PR': 'North America', + 'PS': 'Asia', + 'PT': 'Europe', + 'PW': 'Oceania', + 'PY': 'South America', + 'QA': 'Asia', + 'RE': 'Africa', + 'RO': 'Europe', + 'RS': 'Europe', + 'RU': 'Europe', + 'RW': 'Africa', + 'SA': 'Asia', + 'SB': 'Oceania', + 'SC': 'Africa', + 'SD': 'Africa', + 'SE': 'Europe', + 'SG': 'Asia', + 'SH': 'Africa', + 'SI': 'Europe', + 'SJ': 'Europe', + 'SK': 'Europe', + 'SL': 'Africa', + 'SM': 'Europe', + 'SN': 'Africa', + 'SO': 'Africa', + 'SR': 'South America', + 'SS': 'Africa', + 'ST': 'Africa', + 'SV': 'North America', + 'SY': 'Asia', + 'SZ': 'Africa', + 'TC': 'North America', + 'TD': 'Africa', + 'TG': 'Africa', + 'TH': 'Asia', + 'TJ': 'Asia', + 'TK': 'Oceania', + 'TM': 'Asia', + 'TN': 'Africa', + 'TO': 'Oceania', + 'TP': 'Asia', + 'TR': 'Asia', + 'TT': 'North America', + 'TV': 'Oceania', + 'TW': 'Asia', + 'TZ': 'Africa', + 'UA': 'Europe', + 'UG': 'Africa', + 'US': 'North America', + 'UY': 'South America', + 'UZ': 'Asia', + 'VC': 'North America', + 'VE': 'South America', + 'VG': 'North America', + 'VI': 'North America', + 'VN': 'Asia', + 'VU': 'Oceania', + 'WF': 'Oceania', + 'WS': 'Oceania', + 'XK': 'Europe', + 'YE': 'Asia', + 'YT': 'Africa', + 'ZA': 'Africa', + 'ZM': 'Africa', + 'ZW': 'Africa', +} + + +def country_code_to_continent(country_code): + # Returns the continent a country code belongs to. + return COUNTRY_CODE_TO_CONTINENT[country_code] From ed7343882800878a83c695f4338060bd86155122 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sun, 31 Mar 2024 11:08:27 -0700 Subject: [PATCH 09/32] Update event.py Added location formatting function. --- tools/events-automation/event.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index 49b4fadd2..bd5bcce26 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -1,3 +1,7 @@ +from datetime import datetime +from geopy.geocoders import Nominatim +from state_territory_to_abbrev import au_state_territory_to_abbrev, us_state_to_abbrev, ca_state_territory_to_abbrev +from country_to_abbrev import country_to_abbrev class Event(): def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, popularity=None, recurring=None, inPast=None, duplicate=False) -> None: @@ -18,3 +22,22 @@ def to_markdown_string(self) -> str: else: return f'* {self.date} | {self.location} | [{self.organizerName}](TODO: ORGANISER URL HERE)\n\t*[**{self.name}**]({self.url})' + +def format_location(self): + # Formats location data into (city, +/-state, country). + geocoder = Nominatim(user_agent="TWiR") + locationData = str(geocoder.geocode(self.location, language="en").split(",")) + + if len(locationData) > 3: + city, state, country = locationData[2].strip(), locationData[3].strip(), locationData[-1].strip() + country = country_to_abbrev(country) + if country in ["AU", "CA", "US"]: + if country == "AU": + state = au_state_territory_to_abbrev(state) + elif country == "CA": + state = ca_state_territory_to_abbrev(state) + elif country == "US": + state = us_state_to_abbrev(state) + self.location = f'{city}, {state}, {country}' + else: + self.location = f'{city}, {country}' From ad7e018f656d469e370c38823b3327e4f47ba5f5 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sun, 31 Mar 2024 11:10:40 -0700 Subject: [PATCH 10/32] Create state_territory_to_abbrev.py Adds functions for formatting state/territory alpha_2 code. --- .../state_territory_to_abbrev.py | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 tools/events-automation/state_territory_to_abbrev.py diff --git a/tools/events-automation/state_territory_to_abbrev.py b/tools/events-automation/state_territory_to_abbrev.py new file mode 100644 index 000000000..77f9b6a29 --- /dev/null +++ b/tools/events-automation/state_territory_to_abbrev.py @@ -0,0 +1,102 @@ +# Returns the abbreviated version of state/territory name for AU, CA, and US. +# Information from Wikipedia. + +US_STATE_TO_ABBREV = { + "Alabama": "AL", + "Alaska": "AK", + "Arizona": "AZ", + "Arkansas": "AR", + "California": "CA", + "Colorado": "CO", + "Connecticut": "CT", + "Delaware": "DE", + "Florida": "FL", + "Georgia": "GA", + "Hawaii": "HI", + "Idaho": "ID", + "Illinois": "IL", + "Indiana": "IN", + "Iowa": "IA", + "Kansas": "KS", + "Kentucky": "KY", + "Louisiana": "LA", + "Maine": "ME", + "Maryland": "MD", + "Massachusetts": "MA", + "Michigan": "MI", + "Minnesota": "MN", + "Mississippi": "MS", + "Missouri": "MO", + "Montana": "MT", + "Nebraska": "NE", + "Nevada": "NV", + "New Hampshire": "NH", + "New Jersey": "NJ", + "New Mexico": "NM", + "New York": "NY", + "North Carolina": "NC", + "North Dakota": "ND", + "Ohio": "OH", + "Oklahoma": "OK", + "Oregon": "OR", + "Pennsylvania": "PA", + "Rhode Island": "RI", + "South Carolina": "SC", + "South Dakota": "SD", + "Tennessee": "TN", + "Texas": "TX", + "Utah": "UT", + "Vermont": "VT", + "Virginia": "VA", + "Washington": "WA", + "West Virginia": "WV", + "Wisconsin": "WI", + "Wyoming": "WY", + "District of Columbia": "DC", + "American Samoa": "AS", + "Guam": "GU", + "Northern Mariana Islands": "MP", + "Puerto Rico": "PR", + "United States Minor Outlying Islands": "UM", + "U.S. Virgin Islands": "VI", +} + +CA_STATE_TERRITORY_TO_ABBREV = { + "Alberta": "AB", + "British Columbia": "BC", + "Manitoba": "MB", + "New Brunswick": "NB", + "Newfoundland and Labrador": "NL", + "Northwest Territories": "NT", + "Nova Scotia": "NS", + "Nunavut": "NU", + "Ontario": "ON", + "Prince Edward Island": "PE", + "Quebec": "QC", + "Saskatchewan": "SK", + "Yukon": "YT", +} + +AU_STATE_TERRITORY_TO_ABBREV = { + "New South Wales": "NSW", + "Northern Territory": "NT", + "Queensland": "QLD", + "South Australia": "SA", + "Tasmania": "TAS", + "Victoria": "VIC", + "Western Australia": "WA", +} + +def us_state_to_abbrev(state): + # Returns the abbreviated alpha code for input state. + return US_STATE_TO_ABBREV[state] + + +def ca_state_territory_to_abbrev(state): + # Returns the abbreviated alpha code for input state/territory. + return CA_STATE_TERRITORY_TO_ABBREV[state] + + +def au_state_territory_to_abbrev(state): + # Returns the abbreviated alpha code for input state/territory. + return AU_STATE_TERRITORY_TO_ABBREV[state] From bf7136d86a99cac778c2d4e91fa79de8819fde46 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sun, 31 Mar 2024 11:11:43 -0700 Subject: [PATCH 11/32] Create country_to_abbrev.py Function for formatting a country into it's alpha-2 code. --- tools/events-automation/country_to_abbrev.py | 281 +++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 tools/events-automation/country_to_abbrev.py diff --git a/tools/events-automation/country_to_abbrev.py b/tools/events-automation/country_to_abbrev.py new file mode 100644 index 000000000..dcf3893fe --- /dev/null +++ b/tools/events-automation/country_to_abbrev.py @@ -0,0 +1,281 @@ +# Takes a country and returns it's ISO alpha-2 code. + +COUNTRY_TO_ABBREV = { + 'Abkhazia': 'AB', + 'Afghanistan': 'AF', + 'Albania': 'AL', + 'Algeria': 'DZ', + 'American Samoa': 'AS', + 'Andorra': 'AD', + 'Angola': 'AO', + 'Anguilla': 'AI', + 'Antigua and Barbuda': 'AG', + 'Argentina': 'AR', + 'Armenia': 'AM', + 'Aruba': 'AW', + 'Australia': 'AU', + 'Austria': 'AT', + 'Azerbaijan': 'AZ', + 'Bahamas': 'BS', + 'Bahrain': 'BH', + 'Bangladesh': 'BD', + 'Barbados': 'BB', + 'Belarus': 'BY', + 'Belgium': 'BE', + 'Belize': 'BZ', + 'Benin': 'BJ', + 'Bermuda': 'BM', + 'Bhutan': 'BT', + 'Bolivia': 'BO', + 'Bonaire': 'BQ', + 'Bosnia and Herzegovina': 'BA', + 'Botswana': 'BW', + 'Bouvet Island': 'BV', + 'Brazil': 'BR', + 'British Indian Ocean Territory': 'IO', + 'British Virgin Islands': 'VG', + 'Virgin Islands, British': 'VG', + 'Brunei': 'BN', + 'Brunei Darussalam': 'BN', + 'Bulgaria': 'BG', + 'Burkina Faso': 'BF', + 'Burundi': 'BI', + 'Cambodia': 'KH', + 'Cameroon': 'CM', + 'Canada': 'CA', + 'Cape Verde': 'CV', + 'Cayman Islands': 'KY', + 'Central African Republic': 'CF', + 'Chad': 'TD', + 'Chile': 'CL', + 'China': 'CN', + 'Christmas Island': 'CX', + 'Cocos (Keeling) Islands': 'CC', + 'Colombia': 'CO', + 'Comoros': 'KM', + 'Congo': 'CG', + 'Congo, Republic of': 'CG', + 'Republic of the Congo': 'CG', + 'Cook Islands': 'CK', + 'Costa Rica': 'CR', + 'Croatia': 'HR', + 'Cuba': 'CU', + 'Curaçao': 'CW', + 'Cyprus': 'CY', + 'Czech Republic': 'CZ', + 'Congo, Democratic Republic of': 'CD', + 'Democratic Republic of the Congo': 'CD', + 'Denmark': 'DK', + 'Djibouti': 'DJ', + 'Dominica': 'DM', + 'Dominican Republic': 'DO', + 'East Timor': 'TP', + 'Ecuador': 'EC', + 'Egypt': 'EG', + 'El Salvador': 'SV', + 'Equatorial Guinea': 'GQ', + 'Eritrea': 'ER', + 'Estonia': 'EE', + 'Ethiopia': 'ET', + 'Falkland Islands': 'FK', + 'Faroe Islands': 'FO', + 'Fiji': 'FJ', + 'Finland': 'FI', + 'France': 'FR', + 'French Guiana': 'GF', + 'French Polynesia': 'PF', + 'Gabon': 'GA', + 'Gambia': 'GM', + 'Georgia': 'GE', + 'Germany': 'DE', + 'Ghana': 'GH', + 'Gibraltar': 'GI', + 'Greece': 'GR', + 'Greenland': 'GL', + 'Grenada': 'GD', + 'Guadeloupe': 'GP', + 'Great Britain': 'GB', + 'Guam': 'GU', + 'Guatemala': 'GT', + 'Guernsey': 'GG', + 'Guinea': 'GN', + 'Guinea-Bissau': 'GW', + 'Guyana': 'GY', + 'Haiti': 'HT', + 'Heard Island and McDonald Islands': 'HM', + 'Honduras': 'HN', + 'Hong Kong': 'HK', + 'Hungary': 'HU', + 'Iceland': 'IS', + 'India': 'IN', + 'Indonesia': 'ID', + 'Iran': 'IR', + 'Iraq': 'IQ', + 'Ireland': 'IE', + 'Isle of Man': 'IM', + 'Islamic Republic of Iran': 'IR', + 'Israel': 'IL', + 'Italy': 'IT', + 'Ivory Coast': 'CI', + 'Jamaica': 'JM', + 'Japan': 'JP', + 'Jersey': 'JE', + 'Jordan': 'JO', + 'Kazakhstan': 'KZ', + 'Kenya': 'KE', + "Korea, Democratic People's Republic of": 'KP', + 'Kiribati': 'KI', + 'Korea, Republic Of': 'KR', + 'Kosovo': 'XK', + 'Kuwait': 'KW', + 'Kyrgyzstan': 'KG', + 'Laos': 'LA', + "Lao People's Democratic Republic": 'LA', + 'Latvia': 'LV', + 'Lebanon': 'LB', + 'Lesotho': 'LS', + 'Liberia': 'LR', + 'Libya': 'LY', + 'Liechtenstein': 'LI', + 'Lithuania': 'LT', + 'Luxembourg': 'LU', + 'Macau': 'MO', + 'Macedonia': 'MK', + 'Macedonia, The Former Yugoslav Republic Of': 'MK', + 'Madagascar': 'MG', + 'Malawi': 'MW', + 'Malaysia': 'MY', + 'Maldives': 'MV', + 'Mali': 'ML', + 'Malta': 'MT', + 'Marshall Islands': 'MH', + 'Martinique': 'MQ', + 'Mauritania': 'MR', + 'Mauritius': 'MU', + 'Mayotte': 'YT', + 'Mexico': 'MX', + 'Micronesia': 'FM', + 'Micronesia, Federated States of': 'FM', + 'Moldova': 'MD', + 'Moldova, Republic Of': 'MD', + 'Monaco': 'MC', + 'Mongolia': 'MN', + 'Montenegro': 'ME', + 'Montserrat': 'MS', + 'Morocco': 'MA', + 'Mozambique': 'MZ', + 'Myanmar': 'MM', + 'Namibia': 'NA', + 'Nauru': 'NR', + 'Nepal': 'NP', + 'Netherlands': 'NL', + 'New Caledonia': 'NC', + 'New Zealand': 'NZ', + 'Nicaragua': 'NI', + 'Niger': 'NE', + 'Nigeria': 'NG', + 'Niue': 'NU', + 'Norfolk Island': 'NF', + 'North Korea': 'KP', + 'Northern Cyprus': 'CY', + 'Northern Mariana Islands': 'MP', + 'Norway': 'NO', + 'Oman': 'OM', + 'Pakistan': 'PK', + 'Palau': 'PW', + 'Palestine': 'PS', + 'Panama': 'PA', + 'Papua New Guinea': 'PG', + 'Paraguay': 'PY', + 'Peru': 'PE', + 'Philippines': 'PH', + 'Poland': 'PL', + 'Portugal': 'PT', + 'Puerto Rico': 'PR', + 'Qatar': 'QA', + 'Romania': 'RO', + 'Russia': 'RU', + 'Russian Federation': 'RU', + 'Rwanda': 'RW', + 'Réunion': 'RE', + 'Saba': 'BQ', + 'Saint Barthélemy': 'BL', + 'Saint Helena, Ascension and Tristan da Cunha': 'SH', + 'Saint Kitts and Nevis': 'KN', + 'St. Kitts and Nevis': 'KN', + 'Saint Lucia': 'LC', + 'St. Lucia': 'LC', + 'Saint Martin': 'MF', + 'St. Martin': 'MF', + 'Saint Pierre and Miquelon': 'PM', + 'St. Pierre and Miquelon': 'PM', + 'Saint Vincent and the Grenadines': 'VC', + 'St. Vincent and The Grenadines': 'VC', + 'Samoa': 'WS', + 'San Marino': 'SM', + 'Saudi Arabia': 'SA', + 'Senegal': 'SN', + 'Serbia': 'RS', + 'Seychelles': 'SC', + 'Sierra Leone': 'SL', + 'Singapore': 'SG', + 'Sint Eustatius': 'BQ', + 'Slovakia': 'SK', + 'Slovenia': 'SI', + 'Solomon Islands': 'SB', + 'Somalia': 'SO', + 'Somaliland': 'SO', + 'South Africa': 'ZA', + 'South Georgia and the South Sandwich Islands': 'GS', + 'South Korea': 'KR', + 'South Ossetia': 'OS', + 'South Sudan': 'SS', + 'Spain': 'ES', + 'Sri Lanka': 'LK', + 'Sudan': 'SD', + 'Suriname': 'SR', + 'Svalbard': 'SJ', + 'Swaziland': 'SZ', + 'Sweden': 'SE', + 'Switzerland': 'CH', + 'Syria': 'SY', + 'Syrian Arab Republic': 'SY', + 'São Tomé and Príncipe': 'ST', + 'Taiwan': 'TW', + 'Taiwan, Province of China': 'TW', + 'Tajikistan': 'TJ', + 'Tanzania': 'TZ', + 'Tanzania, United Republic Of': 'TZ', + 'Thailand': 'TH', + 'Togo': 'TG', + 'Tokelau': 'TK', + 'Tonga': 'TO', + 'Trinidad and Tobago': 'TT', + 'Tunisia': 'TN', + 'Turkey': 'TR', + 'Turkmenistan': 'TM', + 'Turks and Caicos Islands': 'TC', + 'Turks and Caicos': 'TC', + 'Tuvalu': 'TV', + 'Uganda': 'UG', + 'Ukraine': 'UA', + 'United Arab Emirates': 'AE', + 'United Kingdom': 'GB', + 'United States Virgin Islands': 'VI', + 'United States': 'US', + 'United States of America': 'US', + 'Uruguay': 'UY', + 'Uzbekistan': 'UZ', + 'Vanuatu': 'VU', + 'Venezuela': 'VE', + 'Vietnam': 'VN', + 'Wallis and Futuna': 'WF', + 'Yemen': 'YE', + 'Zambia': 'ZM', + 'Zimbabwe': 'ZW', + 'Åland Islands': 'AX', +} + + +def country_to_abbrev(country): + return COUNTRY_TO_ABBREV[country] From a57f59bd26418e209e4c5e1b433cb6e20cc1858a Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Mon, 1 Apr 2024 10:59:56 -0700 Subject: [PATCH 12/32] Update main.py Added formatting functions for location and date data. --- tools/events-automation/main.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index b9169b529..970e4c125 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -10,13 +10,21 @@ from country_code_to_continent import country_code_to_continent def main(): + # Get Events list. event_list = get_test_events() + + # Format date and location data. + format_data(event_list) + + # Get closest Wednesday for date range. + start_date = date.today() + while start_date.weekday() != 2: + start_date = start_date + timedelta(days=1) + # Sort Events within date range. # Sorted into virtual or by continent. # Ordered by date, then city. - # NOTE format for start_date is YYYY, MM, DD without unnecessary 0's. - # TODO code user input for window start date for parameter. - event_list = sort_and_filter_events(event_list, date.today()) + event_list = sort_and_filter_events(event_list, start_date) # Output Sorted Event List for continent in event_list: @@ -38,6 +46,13 @@ def main(): print() +def format_data(event_list): + # Formats date and location data into specified format. + for event in event_list: + event.format_date() + event.format_location() + + def sort_and_filter_events(event_list, start_date) -> List[Event]: # Remove Events that are outside current date window. for event in event_list: From e653b0adf64303344758579076068094a46386f8 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:01:17 -0700 Subject: [PATCH 13/32] Update event.py Added formatting function for datetime data. --- tools/events-automation/event.py | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index bd5bcce26..c23ded422 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -22,22 +22,26 @@ def to_markdown_string(self) -> str: else: return f'* {self.date} | {self.location} | [{self.organizerName}](TODO: ORGANISER URL HERE)\n\t*[**{self.name}**]({self.url})' + def format_date(self): + # Formats datetime data into date. + if isinstance(self.date, datetime.datetime): + self.date = self.date.date() -def format_location(self): - # Formats location data into (city, +/-state, country). - geocoder = Nominatim(user_agent="TWiR") - locationData = str(geocoder.geocode(self.location, language="en").split(",")) + def format_location(self): + # Formats location data into (city, +/-state, country). + geocoder = Nominatim(user_agent="TWiR") + locationData = str(geocoder.geocode(self.location, language="en").split(",")) - if len(locationData) > 3: - city, state, country = locationData[2].strip(), locationData[3].strip(), locationData[-1].strip() - country = country_to_abbrev(country) - if country in ["AU", "CA", "US"]: - if country == "AU": - state = au_state_territory_to_abbrev(state) - elif country == "CA": - state = ca_state_territory_to_abbrev(state) - elif country == "US": - state = us_state_to_abbrev(state) - self.location = f'{city}, {state}, {country}' - else: - self.location = f'{city}, {country}' + if len(locationData) > 3: + city, state, country = locationData[2].strip(), locationData[3].strip(), locationData[-1].strip() + country = country_to_abbrev(country) + if country in ["AU", "CA", "US"]: + if country == "AU": + state = au_state_territory_to_abbrev(state) + elif country == "CA": + state = ca_state_territory_to_abbrev(state) + elif country == "US": + state = us_state_to_abbrev(state) + self.location = f'{city}, {state}, {country}' + else: + self.location = f'{city}, {country}' From 32a04678e5ef0ba110dc54b0b26446f6de98c69a Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:15:33 -0700 Subject: [PATCH 14/32] Update README.md Added documentation outlining Event class and Event Sink module. --- tools/events-automation/README.md | 60 ++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tools/events-automation/README.md b/tools/events-automation/README.md index d52c8faea..fda54b43d 100644 --- a/tools/events-automation/README.md +++ b/tools/events-automation/README.md @@ -1 +1,59 @@ -TODO: write this \ No newline at end of file +### Event Class: +**Required Variables** +- name (string): Title of the event. +- location (string): Location of the event either in full detail (e.g. `"111 test st, city, country, postcode"`) to be formatted by `geopy` module and `format_location()` in event sink, or in `"city, state/territory, country"` format with state/territory details included if the location is in Australia, Canada or United States. See code sample included below for an example of location formatting. + - Note: If location string is in `"city, state, country"` format, for both state/territory and country ISO alpha-2 codes must be used (e.g. AU for Australia, CA for California). +- date (date or datetime): Date of event in the locations local time (NOT the local time of where the program is being run). +- url (string): Unique URL for event page details. +- virtual (boolean): If event is online. +- organizerName (string): Name of event organiser. +- organizerUrl (string): URL for event organiser webpage. + +**Additional Variable(s) for Internal Use:** +- duplicate (boolean): Flag for potential event duplicate based on evaluation during event sink. + +**Code Sample for Creating an Event:** +```py +Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url="website3.com", virtual=True, organizerName="Test Organizer", organizerUrl="testorg.com") +``` + +### Within Scope of Event Sink: +- The event sink will take a list of event objects (see `test_events.py` for example data), format the date and location data (if not done already), filter out events that are outside of the pre-determined 'date window' for the current TWiR issue then sort the events by date and then location alphabetically. After this process the list is then split via virtual or continent, and any potential duplicate events within the list are flagged (through comparison of event data). Finally, the event sink will output the details of the finalised list of events in a pre-determined markdown format, complete with virtual/continent headers. +- Note that potential duplicate events will be flagged with a `** NOTE POTENTIAL DUPLICATE: **` warning immediately preceding the event information. + + +### Out of Scope: +- The purpose of the event sink is to cross-reference and curate data from various sources. It shouldn't be responsible for gathering or adding required fields of data into the Event class. Any edge cases should be managed by the event sources. + +### How to Add a New Event Source Module: +- To write a new event source module, it must be written in python. Event sources should be a function that passes no parameters and returns a list of events with required variables detailed above. If the event source has additional requirements, that should be added to `requirements.txt`. The event source should detail any specific run instructions in it's own documentation. Look at `test_events.py` for bare-minimum event list output. +- To run a new event source, import the new module from new event source and add the function call to `event_list`. + +### Requirements to run this code: +- Event sink requires Python installation. +- For specific module requirements: `pip install -r requirements.txt` +- See https://geopy.readthedocs.io/en/stable/# for `geopy` module documentation. +- TODO: Create requirements folder with geopy module, etc. + +### Expected Output: +Example Output from `test_events.py` data: +``` +### Virtual: + +* 2024-04-12 | Virtual (Dublin, IE) | [Test Organizer](testorg.com) + *[**Test Event 1**](website1.com) + +### North America: + +* 2024-04-03 | New York, NY, US | [Test Organizer](testorg.com) + *[**Test Event 7**](website7.com) +* 2024-04-04 | San Francisco, CA, US | [Test Organizer](testorg.com) + *[**Test Event 6**](website6.com) +* 2024-04-18 | Indianapolis, IN, US | [Test Organizer](testorg.com) + *[**Test Event 2**](website2.com) + +### Oceania: + +* 2024-04-04 | Sydney, NSW, AU | [Test Organizer](testorg.com) + *[**Test Event 4**](website4.com) +``` From f104ee2746f2b2c5f3bdfca95ac9a5602a34e174 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:16:08 -0700 Subject: [PATCH 15/32] Update README.md --- tools/events-automation/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/events-automation/README.md b/tools/events-automation/README.md index fda54b43d..7f02ff1a7 100644 --- a/tools/events-automation/README.md +++ b/tools/events-automation/README.md @@ -33,7 +33,6 @@ Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url=" - Event sink requires Python installation. - For specific module requirements: `pip install -r requirements.txt` - See https://geopy.readthedocs.io/en/stable/# for `geopy` module documentation. -- TODO: Create requirements folder with geopy module, etc. ### Expected Output: Example Output from `test_events.py` data: From a0c856adc93554a4ebba1193a4d86eebe2b60ed8 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:18:36 -0700 Subject: [PATCH 16/32] Update README.md --- tools/events-automation/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/events-automation/README.md b/tools/events-automation/README.md index 7f02ff1a7..b0719f84b 100644 --- a/tools/events-automation/README.md +++ b/tools/events-automation/README.md @@ -17,12 +17,13 @@ Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url="website3.com", virtual=True, organizerName="Test Organizer", organizerUrl="testorg.com") ``` -### Within Scope of Event Sink: +### Event Sink: +**Within Scope**: - The event sink will take a list of event objects (see `test_events.py` for example data), format the date and location data (if not done already), filter out events that are outside of the pre-determined 'date window' for the current TWiR issue then sort the events by date and then location alphabetically. After this process the list is then split via virtual or continent, and any potential duplicate events within the list are flagged (through comparison of event data). Finally, the event sink will output the details of the finalised list of events in a pre-determined markdown format, complete with virtual/continent headers. - Note that potential duplicate events will be flagged with a `** NOTE POTENTIAL DUPLICATE: **` warning immediately preceding the event information. -### Out of Scope: +**Out of Scope**: - The purpose of the event sink is to cross-reference and curate data from various sources. It shouldn't be responsible for gathering or adding required fields of data into the Event class. Any edge cases should be managed by the event sources. ### How to Add a New Event Source Module: From 1f96aabf4f753eb693fb1b40fbdd3071d73d7e3e Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:11:49 -0700 Subject: [PATCH 17/32] Create requirements.txt --- tools/events-automation/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/events-automation/requirements.txt diff --git a/tools/events-automation/requirements.txt b/tools/events-automation/requirements.txt new file mode 100644 index 000000000..ac07547e8 --- /dev/null +++ b/tools/events-automation/requirements.txt @@ -0,0 +1 @@ +geopy From b9f7ff1e621478fcd523fd4cb9577100bcf153a5 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:13:26 -0700 Subject: [PATCH 18/32] Update README.md --- tools/events-automation/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/events-automation/README.md b/tools/events-automation/README.md index b0719f84b..fbe49f804 100644 --- a/tools/events-automation/README.md +++ b/tools/events-automation/README.md @@ -1,7 +1,7 @@ ### Event Class: **Required Variables** - name (string): Title of the event. -- location (string): Location of the event either in full detail (e.g. `"111 test st, city, country, postcode"`) to be formatted by `geopy` module and `format_location()` in event sink, or in `"city, state/territory, country"` format with state/territory details included if the location is in Australia, Canada or United States. See code sample included below for an example of location formatting. +- location (string): Location of the event either in full detail (e.g. `"111 test st, city, country, postcode"`) to be formatted by [geopy](https://geopy.readthedocs.io/en/stable/#) module and `format_location()` in event sink, or in `"city, state/territory, country"` format with state/territory details included if the location is in Australia, Canada or United States. See code sample included below for an example of location formatting. - Note: If location string is in `"city, state, country"` format, for both state/territory and country ISO alpha-2 codes must be used (e.g. AU for Australia, CA for California). - date (date or datetime): Date of event in the locations local time (NOT the local time of where the program is being run). - url (string): Unique URL for event page details. @@ -33,7 +33,6 @@ Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url=" ### Requirements to run this code: - Event sink requires Python installation. - For specific module requirements: `pip install -r requirements.txt` -- See https://geopy.readthedocs.io/en/stable/# for `geopy` module documentation. ### Expected Output: Example Output from `test_events.py` data: From 99994125fda23ff7e2ac2cba3513d8ce3348d927 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:20:38 -0700 Subject: [PATCH 19/32] Update event.py --- tools/events-automation/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index c23ded422..3f09e0983 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -30,7 +30,7 @@ def format_date(self): def format_location(self): # Formats location data into (city, +/-state, country). geocoder = Nominatim(user_agent="TWiR") - locationData = str(geocoder.geocode(self.location, language="en").split(",")) + locationData = str(geocoder.geocode(self.location, language="en")).split(",") if len(locationData) > 3: city, state, country = locationData[2].strip(), locationData[3].strip(), locationData[-1].strip() From 28556c6ccaec035e155dfd1af53f045beeff5f66 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Fri, 5 Apr 2024 20:58:27 -0700 Subject: [PATCH 20/32] Update README.md Added sections on the architecture of event sink module and how to use the code. --- tools/events-automation/README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/events-automation/README.md b/tools/events-automation/README.md index fbe49f804..adbfff549 100644 --- a/tools/events-automation/README.md +++ b/tools/events-automation/README.md @@ -22,7 +22,6 @@ Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url=" - The event sink will take a list of event objects (see `test_events.py` for example data), format the date and location data (if not done already), filter out events that are outside of the pre-determined 'date window' for the current TWiR issue then sort the events by date and then location alphabetically. After this process the list is then split via virtual or continent, and any potential duplicate events within the list are flagged (through comparison of event data). Finally, the event sink will output the details of the finalised list of events in a pre-determined markdown format, complete with virtual/continent headers. - Note that potential duplicate events will be flagged with a `** NOTE POTENTIAL DUPLICATE: **` warning immediately preceding the event information. - **Out of Scope**: - The purpose of the event sink is to cross-reference and curate data from various sources. It shouldn't be responsible for gathering or adding required fields of data into the Event class. Any edge cases should be managed by the event sources. @@ -34,6 +33,20 @@ Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url=" - Event sink requires Python installation. - For specific module requirements: `pip install -r requirements.txt` +### How to use this code: +- In order to use the Event Sink code please ensure that all requirements listed above have been met/installed on your device. +- Check that all Event Source module function calls are included in `event_list` (function calls should concatenate into a single list of event objects). + +### Architecture of the Event Sink: +- `main()`: Gathers a list of events from imported Event Source modules, formats the event date and location data, identifies a start date for the date-window filter, creates a 2D sorted list of events from `sort_and_filter_events()`, outputs to screen Event details in markdown format with section headers and duplicate warnings. +- `format_data(event_list)`: Formats event date and location data into pre-determined format (as described above) using Event Class functions `format_date()` and `format_location()`. + - `format_date()`: Identifies if date variable is a datetime object and if so, converts to date object. + - `format_location()`: Identifies if location data is already formatted. If not, parses location data through geopy module, extracts the required information, then formats the data. +- `sort_and_filter_events(event_list, start_date)`: Removes all events that are outside of the determined date window (start_date + 5 weeks), sorts the list of events first by date then by location, identifies potential duplicate events to be manually checked on completion, separates events by virtual/continent into a 2D list that is returned to `main()`. +- `sort_events(event_list)`: Uses an insertion sort algorithm to sort the list of events by date, then by location. +- `potential_duplicate(event_list)`: Flags potential duplicate events by comparing date, url, name, and organizerName variables (in that order). +- `sort_virtual_continent(event_list)`: Separates the event list into a 2D list by virtual/continent. See function code comments for index key. + ### Expected Output: Example Output from `test_events.py` data: ``` From edb6a39453807aefa00bfef7edc7e874ce31bd38 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sat, 11 May 2024 02:00:01 -0700 Subject: [PATCH 21/32] Update event.py added organizerUrl as a class variable. --- tools/events-automation/event.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index 3f09e0983..57c62bef9 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -4,16 +4,14 @@ from country_to_abbrev import country_to_abbrev class Event(): - def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, popularity=None, recurring=None, inPast=None, duplicate=False) -> None: + def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, organizerUrl, duplicate=False) -> None: self.name = name self.location = location self.date = date self.url = url self.virtual = virtual self.organizerName = organizerName - self.popularity = popularity - self.recurring = recurring - self.inPast = inPast + self.organizerUrl = organizerUrl self.duplicate = duplicate def to_markdown_string(self) -> str: From 62ece5985980efd79ef5fc4667bc753f534f2d40 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sat, 11 May 2024 02:37:42 -0700 Subject: [PATCH 22/32] Update main.py Removed sort_and_filter_events() function. Created a more modular structure with individual function calls to handle each task in Event Sink. --- tools/events-automation/main.py | 66 ++++++++++++--------------------- 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index 970e4c125..54ccfaf88 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -10,23 +10,30 @@ from country_code_to_continent import country_code_to_continent def main(): - # Get Events list. + # Get Events list from Event Sources. event_list = get_test_events() # Format date and location data. format_data(event_list) - # Get closest Wednesday for date range. - start_date = date.today() - while start_date.weekday() != 2: - start_date = start_date + timedelta(days=1) + # Remove events outside of date range. + date_window_filter(event_list) - # Sort Events within date range. - # Sorted into virtual or by continent. - # Ordered by date, then city. - event_list = sort_and_filter_events(event_list, start_date) + # Sort remaining events by date, then location. + event_list.sort(key=lambda event: (event.date, event.location)) + + # Flag potential duplicate events. + potential_duplicate(event_list) + + # Sort into virtual or by continent. + event_list = sort_virtual_continent(event_list) # Output Sorted Event List + output_to_screen(event_list) + + +def output_to_screen(event_list): + # Prints sorted Event List to terminal screen. for continent in event_list: if len(continent) == 0: continue @@ -53,43 +60,16 @@ def format_data(event_list): event.format_location() -def sort_and_filter_events(event_list, start_date) -> List[Event]: - # Remove Events that are outside current date window. +def date_window_filter(event_list): + # Removes Events that are outside current date window. + # Date window = closest wednesday + 5 weeks. + start_date = date.today() + while start_date.weekday() != 2: + start_date = start_date + timedelta(days=1) + for event in event_list: if not (start_date <= event.date <= start_date + timedelta(weeks=5)): event_list.remove(event) - - # Sort Events by date and location. - sort_events(event_list) - - # Flag potential duplicate events. - potential_duplicate(event_list) - - # Return 2D list of Events sorted by Virtual/Continent. - return sort_virtual_continent(event_list) - - -def sort_events(event_list): - for i in range(1, len(event_list)): - # Assigns current event to sort. - current_event = event_list[i] - - # Initialise comparison index - j = i - 1 - while j >= 0 and event_list[j].date > current_event.date: - # Shifts all events with lower dates to the left. - event_list[j+1] = event_list[j] - j -= 1 - - if event_list[j].date == current_event.date: - # If current event date is equal to comparison date, compares location. - while j >= 0 and current_event.location[0:3] < event_list[j].location[0:3]: - # Shifts events with a location alphabetically higher to the left. - event_list[j+1] = event_list[j] - j -= 1 - - # Places current event in correct index. - event_list[j+1] = current_event def sort_virtual_continent(event_list) -> List[Event]: From 90987adf5127e0c90ce04e8478860d38bf09d7e9 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sat, 11 May 2024 16:43:56 -0700 Subject: [PATCH 23/32] Update event.py --- tools/events-automation/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index 57c62bef9..1674e875e 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -4,7 +4,7 @@ from country_to_abbrev import country_to_abbrev class Event(): - def __init__(self, name, location, date, url, virtual, organizerName, maybeSpam, organizerUrl, duplicate=False) -> None: + def __init__(self, name, location, date, url, virtual, organizerName, organizerUrl, duplicate=False) -> None: self.name = name self.location = location self.date = date From 90f3abe54e107271795ebcef3e11baf8af1832f4 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Sat, 11 May 2024 17:02:55 -0700 Subject: [PATCH 24/32] Update event.py --- tools/events-automation/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index 1674e875e..3e6ef6516 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -1,4 +1,4 @@ -from datetime import datetime +import datetime from geopy.geocoders import Nominatim from state_territory_to_abbrev import au_state_territory_to_abbrev, us_state_to_abbrev, ca_state_territory_to_abbrev from country_to_abbrev import country_to_abbrev From dc22774924dda0f882979b3757ed6a087ba4706d Mon Sep 17 00:00:00 2001 From: Hannah Cotterell Date: Tue, 28 May 2024 21:04:08 -0700 Subject: [PATCH 25/32] Updates main.py and event.py. Fixes location formatting, refactors grouping function to output a dictionary. --- .../country_code_to_continent.py | 511 ++++++++-------- tools/events-automation/country_to_abbrev.py | 562 +++++++++--------- tools/events-automation/event.py | 34 +- tools/events-automation/main.py | 50 +- .../state_territory_to_abbrev.py | 204 +++---- 5 files changed, 670 insertions(+), 691 deletions(-) diff --git a/tools/events-automation/country_code_to_continent.py b/tools/events-automation/country_code_to_continent.py index 7b254fd5c..03be89754 100644 --- a/tools/events-automation/country_code_to_continent.py +++ b/tools/events-automation/country_code_to_continent.py @@ -1,255 +1,256 @@ -# Takes an ISO alpha-2 country code and returns the continent. -# Mapping of ISO alpha-2 Country Codes to Continent, from Wikipedia. - -COUNTRY_CODE_TO_CONTINENT = { - 'AB': 'Asia', - 'AD': 'Europe', - 'AE': 'Asia', - 'AF': 'Asia', - 'AG': 'North America', - 'AI': 'North America', - 'AL': 'Europe', - 'AM': 'Asia', - 'AO': 'Africa', - 'AR': 'South America', - 'AS': 'Oceania', - 'AT': 'Europe', - 'AU': 'Oceania', - 'AW': 'North America', - 'AX': 'Europe', - 'AZ': 'Asia', - 'BA': 'Europe', - 'BB': 'North America', - 'BD': 'Asia', - 'BE': 'Europe', - 'BF': 'Africa', - 'BG': 'Europe', - 'BH': 'Asia', - 'BI': 'Africa', - 'BJ': 'Africa', - 'BL': 'North America', - 'BM': 'North America', - 'BN': 'Asia', - 'BO': 'South America', - 'BQ': 'North America', - 'BR': 'South America', - 'BS': 'North America', - 'BT': 'Asia', - 'BV': 'Antarctica', - 'BW': 'Africa', - 'BY': 'Europe', - 'BZ': 'North America', - 'CA': 'North America', - 'CC': 'Asia', - 'CD': 'Africa', - 'CF': 'Africa', - 'CG': 'Africa', - 'CH': 'Europe', - 'CI': 'Africa', - 'CK': 'Oceania', - 'CL': 'South America', - 'CM': 'Africa', - 'CN': 'Asia', - 'CO': 'South America', - 'CR': 'North America', - 'CU': 'North America', - 'CV': 'Africa', - 'CW': 'North America', - 'CX': 'Asia', - 'CY': 'Asia', - 'CZ': 'Europe', - 'DE': 'Europe', - 'DJ': 'Africa', - 'DK': 'Europe', - 'DM': 'North America', - 'DO': 'North America', - 'DZ': 'Africa', - 'EC': 'South America', - 'EE': 'Europe', - 'EG': 'Africa', - 'ER': 'Africa', - 'ES': 'Europe', - 'ET': 'Africa', - 'FI': 'Europe', - 'FJ': 'Oceania', - 'FK': 'South America', - 'FM': 'Oceania', - 'FO': 'Europe', - 'FR': 'Europe', - 'GA': 'Africa', - 'GB': 'Europe', - 'GD': 'North America', - 'GE': 'Asia', - 'GF': 'South America', - 'GG': 'Europe', - 'GH': 'Africa', - 'GI': 'Europe', - 'GL': 'North America', - 'GM': 'Africa', - 'GN': 'Africa', - 'GP': 'North America', - 'GQ': 'Africa', - 'GR': 'Europe', - 'GS': 'South America', - 'GT': 'North America', - 'GU': 'Oceania', - 'GW': 'Africa', - 'GY': 'South America', - 'HK': 'Asia', - 'HM': 'Antarctica', - 'HN': 'North America', - 'HR': 'Europe', - 'HT': 'North America', - 'HU': 'Europe', - 'ID': 'Asia', - 'IE': 'Europe', - 'IL': 'Asia', - 'IM': 'Europe', - 'IN': 'Asia', - 'IO': 'Asia', - 'IQ': 'Asia', - 'IR': 'Asia', - 'IS': 'Europe', - 'IT': 'Europe', - 'JE': 'Europe', - 'JM': 'North America', - 'JO': 'Asia', - 'JP': 'Asia', - 'KE': 'Africa', - 'KG': 'Asia', - 'KH': 'Asia', - 'KI': 'Oceania', - 'KM': 'Africa', - 'KN': 'North America', - 'KP': 'Asia', - 'KR': 'Asia', - 'KW': 'Asia', - 'KY': 'North America', - 'KZ': 'Asia', - 'LA': 'Asia', - 'LB': 'Asia', - 'LC': 'North America', - 'LI': 'Europe', - 'LK': 'Asia', - 'LR': 'Africa', - 'LS': 'Africa', - 'LT': 'Europe', - 'LU': 'Europe', - 'LV': 'Europe', - 'LY': 'Africa', - 'MA': 'Africa', - 'MC': 'Europe', - 'MD': 'Europe', - 'ME': 'Europe', - 'MF': 'North America', - 'MG': 'Africa', - 'MH': 'Oceania', - 'MK': 'Europe', - 'ML': 'Africa', - 'MM': 'Asia', - 'MN': 'Asia', - 'MO': 'Asia', - 'MP': 'Oceania', - 'MQ': 'North America', - 'MR': 'Africa', - 'MS': 'North America', - 'MT': 'Europe', - 'MU': 'Africa', - 'MV': 'Asia', - 'MW': 'Africa', - 'MX': 'North America', - 'MY': 'Asia', - 'MZ': 'Africa', - 'NA': 'Africa', - 'NC': 'Oceania', - 'NE': 'Africa', - 'NF': 'Oceania', - 'NG': 'Africa', - 'NI': 'North America', - 'NL': 'Europe', - 'NO': 'Europe', - 'NP': 'Asia', - 'NR': 'Oceania', - 'NU': 'Oceania', - 'NZ': 'Oceania', - 'OM': 'Asia', - 'OS': 'Asia', - 'PA': 'North America', - 'PE': 'South America', - 'PF': 'Oceania', - 'PG': 'Oceania', - 'PH': 'Asia', - 'PK': 'Asia', - 'PL': 'Europe', - 'PM': 'North America', - 'PR': 'North America', - 'PS': 'Asia', - 'PT': 'Europe', - 'PW': 'Oceania', - 'PY': 'South America', - 'QA': 'Asia', - 'RE': 'Africa', - 'RO': 'Europe', - 'RS': 'Europe', - 'RU': 'Europe', - 'RW': 'Africa', - 'SA': 'Asia', - 'SB': 'Oceania', - 'SC': 'Africa', - 'SD': 'Africa', - 'SE': 'Europe', - 'SG': 'Asia', - 'SH': 'Africa', - 'SI': 'Europe', - 'SJ': 'Europe', - 'SK': 'Europe', - 'SL': 'Africa', - 'SM': 'Europe', - 'SN': 'Africa', - 'SO': 'Africa', - 'SR': 'South America', - 'SS': 'Africa', - 'ST': 'Africa', - 'SV': 'North America', - 'SY': 'Asia', - 'SZ': 'Africa', - 'TC': 'North America', - 'TD': 'Africa', - 'TG': 'Africa', - 'TH': 'Asia', - 'TJ': 'Asia', - 'TK': 'Oceania', - 'TM': 'Asia', - 'TN': 'Africa', - 'TO': 'Oceania', - 'TP': 'Asia', - 'TR': 'Asia', - 'TT': 'North America', - 'TV': 'Oceania', - 'TW': 'Asia', - 'TZ': 'Africa', - 'UA': 'Europe', - 'UG': 'Africa', - 'US': 'North America', - 'UY': 'South America', - 'UZ': 'Asia', - 'VC': 'North America', - 'VE': 'South America', - 'VG': 'North America', - 'VI': 'North America', - 'VN': 'Asia', - 'VU': 'Oceania', - 'WF': 'Oceania', - 'WS': 'Oceania', - 'XK': 'Europe', - 'YE': 'Asia', - 'YT': 'Africa', - 'ZA': 'Africa', - 'ZM': 'Africa', - 'ZW': 'Africa', -} - - -def country_code_to_continent(country_code): - # Returns the continent a country code belongs to. - return COUNTRY_CODE_TO_CONTINENT[country_code] +# Takes an ISO alpha-2 country code and returns the continent. +# Mapping of ISO alpha-2 Country Codes to Continent, from Wikipedia. + +COUNTRY_CODE_TO_CONTINENT = { + 'AB': 'Asia', + 'AD': 'Europe', + 'AE': 'Asia', + 'AF': 'Asia', + 'AG': 'North America', + 'AI': 'North America', + 'AL': 'Europe', + 'AM': 'Asia', + 'AO': 'Africa', + 'AR': 'South America', + 'AS': 'Oceania', + 'AT': 'Europe', + 'AU': 'Oceania', + 'AW': 'North America', + 'AX': 'Europe', + 'AZ': 'Asia', + 'BA': 'Europe', + 'BB': 'North America', + 'BD': 'Asia', + 'BE': 'Europe', + 'BF': 'Africa', + 'BG': 'Europe', + 'BH': 'Asia', + 'BI': 'Africa', + 'BJ': 'Africa', + 'BL': 'North America', + 'BM': 'North America', + 'BN': 'Asia', + 'BO': 'South America', + 'BQ': 'North America', + 'BR': 'South America', + 'BS': 'North America', + 'BT': 'Asia', + 'BV': 'Antarctica', + 'BW': 'Africa', + 'BY': 'Europe', + 'BZ': 'North America', + 'CA': 'North America', + 'CC': 'Asia', + 'CD': 'Africa', + 'CF': 'Africa', + 'CG': 'Africa', + 'CH': 'Europe', + 'CI': 'Africa', + 'CK': 'Oceania', + 'CL': 'South America', + 'CM': 'Africa', + 'CN': 'Asia', + 'CO': 'South America', + 'CR': 'North America', + 'CU': 'North America', + 'CV': 'Africa', + 'CW': 'North America', + 'CX': 'Asia', + 'CY': 'Asia', + 'CZ': 'Europe', + 'DE': 'Europe', + 'DJ': 'Africa', + 'DK': 'Europe', + 'DM': 'North America', + 'DO': 'North America', + 'DZ': 'Africa', + 'EC': 'South America', + 'EE': 'Europe', + 'EG': 'Africa', + 'ER': 'Africa', + 'ES': 'Europe', + 'ET': 'Africa', + 'FI': 'Europe', + 'FJ': 'Oceania', + 'FK': 'South America', + 'FM': 'Oceania', + 'FO': 'Europe', + 'FR': 'Europe', + 'GA': 'Africa', + 'GB': 'Europe', + 'GD': 'North America', + 'GE': 'Asia', + 'GF': 'South America', + 'GG': 'Europe', + 'GH': 'Africa', + 'GI': 'Europe', + 'GL': 'North America', + 'GM': 'Africa', + 'GN': 'Africa', + 'GP': 'North America', + 'GQ': 'Africa', + 'GR': 'Europe', + 'GS': 'South America', + 'GT': 'North America', + 'GU': 'Oceania', + 'GW': 'Africa', + 'GY': 'South America', + 'HK': 'Asia', + 'HM': 'Antarctica', + 'HN': 'North America', + 'HR': 'Europe', + 'HT': 'North America', + 'HU': 'Europe', + 'ID': 'Asia', + 'IE': 'Europe', + 'IL': 'Asia', + 'IM': 'Europe', + 'IN': 'Asia', + 'IO': 'Asia', + 'IQ': 'Asia', + 'IR': 'Asia', + 'IS': 'Europe', + 'IT': 'Europe', + 'JE': 'Europe', + 'JM': 'North America', + 'JO': 'Asia', + 'JP': 'Asia', + 'KE': 'Africa', + 'KG': 'Asia', + 'KH': 'Asia', + 'KI': 'Oceania', + 'KM': 'Africa', + 'KN': 'North America', + 'KP': 'Asia', + 'KR': 'Asia', + 'KW': 'Asia', + 'KY': 'North America', + 'KZ': 'Asia', + 'LA': 'Asia', + 'LB': 'Asia', + 'LC': 'North America', + 'LI': 'Europe', + 'LK': 'Asia', + 'LR': 'Africa', + 'LS': 'Africa', + 'LT': 'Europe', + 'LU': 'Europe', + 'LV': 'Europe', + 'LY': 'Africa', + 'MA': 'Africa', + 'MC': 'Europe', + 'MD': 'Europe', + 'ME': 'Europe', + 'MF': 'North America', + 'MG': 'Africa', + 'MH': 'Oceania', + 'MK': 'Europe', + 'ML': 'Africa', + 'MM': 'Asia', + 'MN': 'Asia', + 'MO': 'Asia', + 'MP': 'Oceania', + 'MQ': 'North America', + 'MR': 'Africa', + 'MS': 'North America', + 'MT': 'Europe', + 'MU': 'Africa', + 'MV': 'Asia', + 'MW': 'Africa', + 'MX': 'North America', + 'MY': 'Asia', + 'MZ': 'Africa', + 'NA': 'Africa', + 'NC': 'Oceania', + 'NE': 'Africa', + 'NF': 'Oceania', + 'NG': 'Africa', + 'NI': 'North America', + 'NL': 'Europe', + 'NO': 'Europe', + 'NP': 'Asia', + 'NR': 'Oceania', + 'NU': 'Oceania', + 'NZ': 'Oceania', + 'OM': 'Asia', + 'OS': 'Asia', + 'PA': 'North America', + 'PE': 'South America', + 'PF': 'Oceania', + 'PG': 'Oceania', + 'PH': 'Asia', + 'PK': 'Asia', + 'PL': 'Europe', + 'PM': 'North America', + 'PR': 'North America', + 'PS': 'Asia', + 'PT': 'Europe', + 'PW': 'Oceania', + 'PY': 'South America', + 'QA': 'Asia', + 'RE': 'Africa', + 'RO': 'Europe', + 'RS': 'Europe', + 'RU': 'Europe', + 'RW': 'Africa', + 'SA': 'Asia', + 'SB': 'Oceania', + 'SC': 'Africa', + 'SD': 'Africa', + 'SE': 'Europe', + 'SG': 'Asia', + 'SH': 'Africa', + 'SI': 'Europe', + 'SJ': 'Europe', + 'SK': 'Europe', + 'SL': 'Africa', + 'SM': 'Europe', + 'SN': 'Africa', + 'SO': 'Africa', + 'SR': 'South America', + 'SS': 'Africa', + 'ST': 'Africa', + 'SV': 'North America', + 'SY': 'Asia', + 'SZ': 'Africa', + 'TC': 'North America', + 'TD': 'Africa', + 'TG': 'Africa', + 'TH': 'Asia', + 'TJ': 'Asia', + 'TK': 'Oceania', + 'TM': 'Asia', + 'TN': 'Africa', + 'TO': 'Oceania', + 'TP': 'Asia', + 'TR': 'Asia', + 'TT': 'North America', + 'TV': 'Oceania', + 'TW': 'Asia', + 'TZ': 'Africa', + 'UA': 'Europe', + 'UG': 'Africa', + 'US': 'North America', + 'UY': 'South America', + 'UZ': 'Asia', + 'VC': 'North America', + 'VE': 'South America', + 'VG': 'North America', + 'VI': 'North America', + 'VN': 'Asia', + 'VU': 'Oceania', + 'WF': 'Oceania', + 'WS': 'Oceania', + 'XK': 'Europe', + 'YE': 'Asia', + 'YT': 'Africa', + 'ZA': 'Africa', + 'ZM': 'Africa', + 'ZW': 'Africa', +} + + +def country_code_to_continent(country_code): + # Returns the continent a country code belongs to. + return COUNTRY_CODE_TO_CONTINENT[country_code] + diff --git a/tools/events-automation/country_to_abbrev.py b/tools/events-automation/country_to_abbrev.py index dcf3893fe..6168f18bf 100644 --- a/tools/events-automation/country_to_abbrev.py +++ b/tools/events-automation/country_to_abbrev.py @@ -1,281 +1,281 @@ -# Takes a country and returns it's ISO alpha-2 code. - -COUNTRY_TO_ABBREV = { - 'Abkhazia': 'AB', - 'Afghanistan': 'AF', - 'Albania': 'AL', - 'Algeria': 'DZ', - 'American Samoa': 'AS', - 'Andorra': 'AD', - 'Angola': 'AO', - 'Anguilla': 'AI', - 'Antigua and Barbuda': 'AG', - 'Argentina': 'AR', - 'Armenia': 'AM', - 'Aruba': 'AW', - 'Australia': 'AU', - 'Austria': 'AT', - 'Azerbaijan': 'AZ', - 'Bahamas': 'BS', - 'Bahrain': 'BH', - 'Bangladesh': 'BD', - 'Barbados': 'BB', - 'Belarus': 'BY', - 'Belgium': 'BE', - 'Belize': 'BZ', - 'Benin': 'BJ', - 'Bermuda': 'BM', - 'Bhutan': 'BT', - 'Bolivia': 'BO', - 'Bonaire': 'BQ', - 'Bosnia and Herzegovina': 'BA', - 'Botswana': 'BW', - 'Bouvet Island': 'BV', - 'Brazil': 'BR', - 'British Indian Ocean Territory': 'IO', - 'British Virgin Islands': 'VG', - 'Virgin Islands, British': 'VG', - 'Brunei': 'BN', - 'Brunei Darussalam': 'BN', - 'Bulgaria': 'BG', - 'Burkina Faso': 'BF', - 'Burundi': 'BI', - 'Cambodia': 'KH', - 'Cameroon': 'CM', - 'Canada': 'CA', - 'Cape Verde': 'CV', - 'Cayman Islands': 'KY', - 'Central African Republic': 'CF', - 'Chad': 'TD', - 'Chile': 'CL', - 'China': 'CN', - 'Christmas Island': 'CX', - 'Cocos (Keeling) Islands': 'CC', - 'Colombia': 'CO', - 'Comoros': 'KM', - 'Congo': 'CG', - 'Congo, Republic of': 'CG', - 'Republic of the Congo': 'CG', - 'Cook Islands': 'CK', - 'Costa Rica': 'CR', - 'Croatia': 'HR', - 'Cuba': 'CU', - 'Curaçao': 'CW', - 'Cyprus': 'CY', - 'Czech Republic': 'CZ', - 'Congo, Democratic Republic of': 'CD', - 'Democratic Republic of the Congo': 'CD', - 'Denmark': 'DK', - 'Djibouti': 'DJ', - 'Dominica': 'DM', - 'Dominican Republic': 'DO', - 'East Timor': 'TP', - 'Ecuador': 'EC', - 'Egypt': 'EG', - 'El Salvador': 'SV', - 'Equatorial Guinea': 'GQ', - 'Eritrea': 'ER', - 'Estonia': 'EE', - 'Ethiopia': 'ET', - 'Falkland Islands': 'FK', - 'Faroe Islands': 'FO', - 'Fiji': 'FJ', - 'Finland': 'FI', - 'France': 'FR', - 'French Guiana': 'GF', - 'French Polynesia': 'PF', - 'Gabon': 'GA', - 'Gambia': 'GM', - 'Georgia': 'GE', - 'Germany': 'DE', - 'Ghana': 'GH', - 'Gibraltar': 'GI', - 'Greece': 'GR', - 'Greenland': 'GL', - 'Grenada': 'GD', - 'Guadeloupe': 'GP', - 'Great Britain': 'GB', - 'Guam': 'GU', - 'Guatemala': 'GT', - 'Guernsey': 'GG', - 'Guinea': 'GN', - 'Guinea-Bissau': 'GW', - 'Guyana': 'GY', - 'Haiti': 'HT', - 'Heard Island and McDonald Islands': 'HM', - 'Honduras': 'HN', - 'Hong Kong': 'HK', - 'Hungary': 'HU', - 'Iceland': 'IS', - 'India': 'IN', - 'Indonesia': 'ID', - 'Iran': 'IR', - 'Iraq': 'IQ', - 'Ireland': 'IE', - 'Isle of Man': 'IM', - 'Islamic Republic of Iran': 'IR', - 'Israel': 'IL', - 'Italy': 'IT', - 'Ivory Coast': 'CI', - 'Jamaica': 'JM', - 'Japan': 'JP', - 'Jersey': 'JE', - 'Jordan': 'JO', - 'Kazakhstan': 'KZ', - 'Kenya': 'KE', - "Korea, Democratic People's Republic of": 'KP', - 'Kiribati': 'KI', - 'Korea, Republic Of': 'KR', - 'Kosovo': 'XK', - 'Kuwait': 'KW', - 'Kyrgyzstan': 'KG', - 'Laos': 'LA', - "Lao People's Democratic Republic": 'LA', - 'Latvia': 'LV', - 'Lebanon': 'LB', - 'Lesotho': 'LS', - 'Liberia': 'LR', - 'Libya': 'LY', - 'Liechtenstein': 'LI', - 'Lithuania': 'LT', - 'Luxembourg': 'LU', - 'Macau': 'MO', - 'Macedonia': 'MK', - 'Macedonia, The Former Yugoslav Republic Of': 'MK', - 'Madagascar': 'MG', - 'Malawi': 'MW', - 'Malaysia': 'MY', - 'Maldives': 'MV', - 'Mali': 'ML', - 'Malta': 'MT', - 'Marshall Islands': 'MH', - 'Martinique': 'MQ', - 'Mauritania': 'MR', - 'Mauritius': 'MU', - 'Mayotte': 'YT', - 'Mexico': 'MX', - 'Micronesia': 'FM', - 'Micronesia, Federated States of': 'FM', - 'Moldova': 'MD', - 'Moldova, Republic Of': 'MD', - 'Monaco': 'MC', - 'Mongolia': 'MN', - 'Montenegro': 'ME', - 'Montserrat': 'MS', - 'Morocco': 'MA', - 'Mozambique': 'MZ', - 'Myanmar': 'MM', - 'Namibia': 'NA', - 'Nauru': 'NR', - 'Nepal': 'NP', - 'Netherlands': 'NL', - 'New Caledonia': 'NC', - 'New Zealand': 'NZ', - 'Nicaragua': 'NI', - 'Niger': 'NE', - 'Nigeria': 'NG', - 'Niue': 'NU', - 'Norfolk Island': 'NF', - 'North Korea': 'KP', - 'Northern Cyprus': 'CY', - 'Northern Mariana Islands': 'MP', - 'Norway': 'NO', - 'Oman': 'OM', - 'Pakistan': 'PK', - 'Palau': 'PW', - 'Palestine': 'PS', - 'Panama': 'PA', - 'Papua New Guinea': 'PG', - 'Paraguay': 'PY', - 'Peru': 'PE', - 'Philippines': 'PH', - 'Poland': 'PL', - 'Portugal': 'PT', - 'Puerto Rico': 'PR', - 'Qatar': 'QA', - 'Romania': 'RO', - 'Russia': 'RU', - 'Russian Federation': 'RU', - 'Rwanda': 'RW', - 'Réunion': 'RE', - 'Saba': 'BQ', - 'Saint Barthélemy': 'BL', - 'Saint Helena, Ascension and Tristan da Cunha': 'SH', - 'Saint Kitts and Nevis': 'KN', - 'St. Kitts and Nevis': 'KN', - 'Saint Lucia': 'LC', - 'St. Lucia': 'LC', - 'Saint Martin': 'MF', - 'St. Martin': 'MF', - 'Saint Pierre and Miquelon': 'PM', - 'St. Pierre and Miquelon': 'PM', - 'Saint Vincent and the Grenadines': 'VC', - 'St. Vincent and The Grenadines': 'VC', - 'Samoa': 'WS', - 'San Marino': 'SM', - 'Saudi Arabia': 'SA', - 'Senegal': 'SN', - 'Serbia': 'RS', - 'Seychelles': 'SC', - 'Sierra Leone': 'SL', - 'Singapore': 'SG', - 'Sint Eustatius': 'BQ', - 'Slovakia': 'SK', - 'Slovenia': 'SI', - 'Solomon Islands': 'SB', - 'Somalia': 'SO', - 'Somaliland': 'SO', - 'South Africa': 'ZA', - 'South Georgia and the South Sandwich Islands': 'GS', - 'South Korea': 'KR', - 'South Ossetia': 'OS', - 'South Sudan': 'SS', - 'Spain': 'ES', - 'Sri Lanka': 'LK', - 'Sudan': 'SD', - 'Suriname': 'SR', - 'Svalbard': 'SJ', - 'Swaziland': 'SZ', - 'Sweden': 'SE', - 'Switzerland': 'CH', - 'Syria': 'SY', - 'Syrian Arab Republic': 'SY', - 'São Tomé and Príncipe': 'ST', - 'Taiwan': 'TW', - 'Taiwan, Province of China': 'TW', - 'Tajikistan': 'TJ', - 'Tanzania': 'TZ', - 'Tanzania, United Republic Of': 'TZ', - 'Thailand': 'TH', - 'Togo': 'TG', - 'Tokelau': 'TK', - 'Tonga': 'TO', - 'Trinidad and Tobago': 'TT', - 'Tunisia': 'TN', - 'Turkey': 'TR', - 'Turkmenistan': 'TM', - 'Turks and Caicos Islands': 'TC', - 'Turks and Caicos': 'TC', - 'Tuvalu': 'TV', - 'Uganda': 'UG', - 'Ukraine': 'UA', - 'United Arab Emirates': 'AE', - 'United Kingdom': 'GB', - 'United States Virgin Islands': 'VI', - 'United States': 'US', - 'United States of America': 'US', - 'Uruguay': 'UY', - 'Uzbekistan': 'UZ', - 'Vanuatu': 'VU', - 'Venezuela': 'VE', - 'Vietnam': 'VN', - 'Wallis and Futuna': 'WF', - 'Yemen': 'YE', - 'Zambia': 'ZM', - 'Zimbabwe': 'ZW', - 'Åland Islands': 'AX', -} - - -def country_to_abbrev(country): - return COUNTRY_TO_ABBREV[country] +# Takes a country and returns it's ISO alpha-2 code. + +COUNTRY_TO_ABBREV = { + 'Abkhazia': 'AB', + 'Afghanistan': 'AF', + 'Albania': 'AL', + 'Algeria': 'DZ', + 'American Samoa': 'AS', + 'Andorra': 'AD', + 'Angola': 'AO', + 'Anguilla': 'AI', + 'Antigua and Barbuda': 'AG', + 'Argentina': 'AR', + 'Armenia': 'AM', + 'Aruba': 'AW', + 'Australia': 'AU', + 'Austria': 'AT', + 'Azerbaijan': 'AZ', + 'Bahamas': 'BS', + 'Bahrain': 'BH', + 'Bangladesh': 'BD', + 'Barbados': 'BB', + 'Belarus': 'BY', + 'Belgium': 'BE', + 'Belize': 'BZ', + 'Benin': 'BJ', + 'Bermuda': 'BM', + 'Bhutan': 'BT', + 'Bolivia': 'BO', + 'Bonaire': 'BQ', + 'Bosnia and Herzegovina': 'BA', + 'Botswana': 'BW', + 'Bouvet Island': 'BV', + 'Brazil': 'BR', + 'British Indian Ocean Territory': 'IO', + 'British Virgin Islands': 'VG', + 'Virgin Islands, British': 'VG', + 'Brunei': 'BN', + 'Brunei Darussalam': 'BN', + 'Bulgaria': 'BG', + 'Burkina Faso': 'BF', + 'Burundi': 'BI', + 'Cambodia': 'KH', + 'Cameroon': 'CM', + 'Canada': 'CA', + 'Cape Verde': 'CV', + 'Cayman Islands': 'KY', + 'Central African Republic': 'CF', + 'Chad': 'TD', + 'Chile': 'CL', + 'China': 'CN', + 'Christmas Island': 'CX', + 'Cocos (Keeling) Islands': 'CC', + 'Colombia': 'CO', + 'Comoros': 'KM', + 'Congo': 'CG', + 'Congo, Republic of': 'CG', + 'Republic of the Congo': 'CG', + 'Cook Islands': 'CK', + 'Costa Rica': 'CR', + 'Croatia': 'HR', + 'Cuba': 'CU', + 'Curaçao': 'CW', + 'Cyprus': 'CY', + 'Czech Republic': 'CZ', + 'Congo, Democratic Republic of': 'CD', + 'Democratic Republic of the Congo': 'CD', + 'Denmark': 'DK', + 'Djibouti': 'DJ', + 'Dominica': 'DM', + 'Dominican Republic': 'DO', + 'East Timor': 'TP', + 'Ecuador': 'EC', + 'Egypt': 'EG', + 'El Salvador': 'SV', + 'Equatorial Guinea': 'GQ', + 'Eritrea': 'ER', + 'Estonia': 'EE', + 'Ethiopia': 'ET', + 'Falkland Islands': 'FK', + 'Faroe Islands': 'FO', + 'Fiji': 'FJ', + 'Finland': 'FI', + 'France': 'FR', + 'French Guiana': 'GF', + 'French Polynesia': 'PF', + 'Gabon': 'GA', + 'Gambia': 'GM', + 'Georgia': 'GE', + 'Germany': 'DE', + 'Ghana': 'GH', + 'Gibraltar': 'GI', + 'Greece': 'GR', + 'Greenland': 'GL', + 'Grenada': 'GD', + 'Guadeloupe': 'GP', + 'Great Britain': 'GB', + 'Guam': 'GU', + 'Guatemala': 'GT', + 'Guernsey': 'GG', + 'Guinea': 'GN', + 'Guinea-Bissau': 'GW', + 'Guyana': 'GY', + 'Haiti': 'HT', + 'Heard Island and McDonald Islands': 'HM', + 'Honduras': 'HN', + 'Hong Kong': 'HK', + 'Hungary': 'HU', + 'Iceland': 'IS', + 'India': 'IN', + 'Indonesia': 'ID', + 'Iran': 'IR', + 'Iraq': 'IQ', + 'Ireland': 'IE', + 'Isle of Man': 'IM', + 'Islamic Republic of Iran': 'IR', + 'Israel': 'IL', + 'Italy': 'IT', + 'Ivory Coast': 'CI', + 'Jamaica': 'JM', + 'Japan': 'JP', + 'Jersey': 'JE', + 'Jordan': 'JO', + 'Kazakhstan': 'KZ', + 'Kenya': 'KE', + "Korea, Democratic People's Republic of": 'KP', + 'Kiribati': 'KI', + 'Korea, Republic Of': 'KR', + 'Kosovo': 'XK', + 'Kuwait': 'KW', + 'Kyrgyzstan': 'KG', + 'Laos': 'LA', + "Lao People's Democratic Republic": 'LA', + 'Latvia': 'LV', + 'Lebanon': 'LB', + 'Lesotho': 'LS', + 'Liberia': 'LR', + 'Libya': 'LY', + 'Liechtenstein': 'LI', + 'Lithuania': 'LT', + 'Luxembourg': 'LU', + 'Macau': 'MO', + 'Macedonia': 'MK', + 'Macedonia, The Former Yugoslav Republic Of': 'MK', + 'Madagascar': 'MG', + 'Malawi': 'MW', + 'Malaysia': 'MY', + 'Maldives': 'MV', + 'Mali': 'ML', + 'Malta': 'MT', + 'Marshall Islands': 'MH', + 'Martinique': 'MQ', + 'Mauritania': 'MR', + 'Mauritius': 'MU', + 'Mayotte': 'YT', + 'Mexico': 'MX', + 'Micronesia': 'FM', + 'Micronesia, Federated States of': 'FM', + 'Moldova': 'MD', + 'Moldova, Republic Of': 'MD', + 'Monaco': 'MC', + 'Mongolia': 'MN', + 'Montenegro': 'ME', + 'Montserrat': 'MS', + 'Morocco': 'MA', + 'Mozambique': 'MZ', + 'Myanmar': 'MM', + 'Namibia': 'NA', + 'Nauru': 'NR', + 'Nepal': 'NP', + 'Netherlands': 'NL', + 'New Caledonia': 'NC', + 'New Zealand': 'NZ', + 'Nicaragua': 'NI', + 'Niger': 'NE', + 'Nigeria': 'NG', + 'Niue': 'NU', + 'Norfolk Island': 'NF', + 'North Korea': 'KP', + 'Northern Cyprus': 'CY', + 'Northern Mariana Islands': 'MP', + 'Norway': 'NO', + 'Oman': 'OM', + 'Pakistan': 'PK', + 'Palau': 'PW', + 'Palestine': 'PS', + 'Panama': 'PA', + 'Papua New Guinea': 'PG', + 'Paraguay': 'PY', + 'Peru': 'PE', + 'Philippines': 'PH', + 'Poland': 'PL', + 'Portugal': 'PT', + 'Puerto Rico': 'PR', + 'Qatar': 'QA', + 'Romania': 'RO', + 'Russia': 'RU', + 'Russian Federation': 'RU', + 'Rwanda': 'RW', + 'Réunion': 'RE', + 'Saba': 'BQ', + 'Saint Barthélemy': 'BL', + 'Saint Helena, Ascension and Tristan da Cunha': 'SH', + 'Saint Kitts and Nevis': 'KN', + 'St. Kitts and Nevis': 'KN', + 'Saint Lucia': 'LC', + 'St. Lucia': 'LC', + 'Saint Martin': 'MF', + 'St. Martin': 'MF', + 'Saint Pierre and Miquelon': 'PM', + 'St. Pierre and Miquelon': 'PM', + 'Saint Vincent and the Grenadines': 'VC', + 'St. Vincent and The Grenadines': 'VC', + 'Samoa': 'WS', + 'San Marino': 'SM', + 'Saudi Arabia': 'SA', + 'Senegal': 'SN', + 'Serbia': 'RS', + 'Seychelles': 'SC', + 'Sierra Leone': 'SL', + 'Singapore': 'SG', + 'Sint Eustatius': 'BQ', + 'Slovakia': 'SK', + 'Slovenia': 'SI', + 'Solomon Islands': 'SB', + 'Somalia': 'SO', + 'Somaliland': 'SO', + 'South Africa': 'ZA', + 'South Georgia and the South Sandwich Islands': 'GS', + 'South Korea': 'KR', + 'South Ossetia': 'OS', + 'South Sudan': 'SS', + 'Spain': 'ES', + 'Sri Lanka': 'LK', + 'Sudan': 'SD', + 'Suriname': 'SR', + 'Svalbard': 'SJ', + 'Swaziland': 'SZ', + 'Sweden': 'SE', + 'Switzerland': 'CH', + 'Syria': 'SY', + 'Syrian Arab Republic': 'SY', + 'São Tomé and Príncipe': 'ST', + 'Taiwan': 'TW', + 'Taiwan, Province of China': 'TW', + 'Tajikistan': 'TJ', + 'Tanzania': 'TZ', + 'Tanzania, United Republic Of': 'TZ', + 'Thailand': 'TH', + 'Togo': 'TG', + 'Tokelau': 'TK', + 'Tonga': 'TO', + 'Trinidad and Tobago': 'TT', + 'Tunisia': 'TN', + 'Turkey': 'TR', + 'Turkmenistan': 'TM', + 'Turks and Caicos Islands': 'TC', + 'Turks and Caicos': 'TC', + 'Tuvalu': 'TV', + 'Uganda': 'UG', + 'Ukraine': 'UA', + 'United Arab Emirates': 'AE', + 'United Kingdom': 'GB', + 'United States Virgin Islands': 'VI', + 'United States': 'US', + 'United States of America': 'US', + 'Uruguay': 'UY', + 'Uzbekistan': 'UZ', + 'Vanuatu': 'VU', + 'Venezuela': 'VE', + 'Vietnam': 'VN', + 'Wallis and Futuna': 'WF', + 'Yemen': 'YE', + 'Zambia': 'ZM', + 'Zimbabwe': 'ZW', + 'Åland Islands': 'AX', +} + + +def country_to_abbrev(country): + return COUNTRY_TO_ABBREV[country] diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index 3e6ef6516..e2f62a9f2 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -1,7 +1,6 @@ import datetime from geopy.geocoders import Nominatim from state_territory_to_abbrev import au_state_territory_to_abbrev, us_state_to_abbrev, ca_state_territory_to_abbrev -from country_to_abbrev import country_to_abbrev class Event(): def __init__(self, name, location, date, url, virtual, organizerName, organizerUrl, duplicate=False) -> None: @@ -16,9 +15,9 @@ def __init__(self, name, location, date, url, virtual, organizerName, organizerU def to_markdown_string(self) -> str: if self.virtual: - return f'* {self.date} | Virtual ({self.location}) | [{self.organizerName}](TODO: ORGANISER URL HERE)\n\t*[**{self.name}**]({self.url})' + return f'* {self.date} | Virtual ({self.location}) | [{self.organizerName}]({self.organizerUrl})\n\t*[**{self.name}**]({self.url})' else: - return f'* {self.date} | {self.location} | [{self.organizerName}](TODO: ORGANISER URL HERE)\n\t*[**{self.name}**]({self.url})' + return f'* {self.date} | {self.location} | [{self.organizerName}]({self.organizerUrl})\n\t*[**{self.name}**]({self.url})' def format_date(self): # Formats datetime data into date. @@ -28,18 +27,19 @@ def format_date(self): def format_location(self): # Formats location data into (city, +/-state, country). geocoder = Nominatim(user_agent="TWiR") - locationData = str(geocoder.geocode(self.location, language="en")).split(",") + locationData = geocoder.geocode(self.location, language="en", addressdetails=True).raw["address"] + + country_code, city = locationData["country_code"].upper(), locationData["city"] + if country_code in ["AU", "CA", "US"]: + state = locationData["state"] + if country_code == "AU": + state_abbrev = au_state_territory_to_abbrev(state) + elif country_code == "CA": + state_abbrev = ca_state_territory_to_abbrev(state) + elif country_code == "US": + state_abbrev = us_state_to_abbrev(state) + self.location = f'{city}, {state_abbrev}, {country_code}' + else: + self.location = f'{city}, {country_code}' + - if len(locationData) > 3: - city, state, country = locationData[2].strip(), locationData[3].strip(), locationData[-1].strip() - country = country_to_abbrev(country) - if country in ["AU", "CA", "US"]: - if country == "AU": - state = au_state_territory_to_abbrev(state) - elif country == "CA": - state = ca_state_territory_to_abbrev(state) - elif country == "US": - state = us_state_to_abbrev(state) - self.location = f'{city}, {state}, {country}' - else: - self.location = f'{city}, {country}' diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index 54ccfaf88..940d6c6a6 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -3,12 +3,12 @@ # call event sink with our collected events # print to console / output to file formatted markdown -from typing import List -from event import Event from test_events import get_test_events from datetime import date, timedelta from country_code_to_continent import country_code_to_continent +# TODO: Flagged events list handling. + def main(): # Get Events list from Event Sources. event_list = get_test_events() @@ -26,27 +26,22 @@ def main(): potential_duplicate(event_list) # Sort into virtual or by continent. - event_list = sort_virtual_continent(event_list) + event_list = group_virtual_continent(event_list) - # Output Sorted Event List + # Output Sorted Event List. output_to_screen(event_list) def output_to_screen(event_list): # Prints sorted Event List to terminal screen. - for continent in event_list: - if len(continent) == 0: + for key, value in event_list.items(): + if len(value) == 0: continue else: - country_code = continent[0].location[-2:] - # Output Section Header - if continent[0].virtual: - print(f'### Virtual:\n') - else: - print(f'### {country_code_to_continent(country_code)}:\n') + print(f'### {key}:\n') # Output event details - for event in continent: + for event in value: if event.duplicate: print("** NOTE POTENTIAL DUPLICATE: **") print(event.to_markdown_string()) @@ -72,32 +67,15 @@ def date_window_filter(event_list): event_list.remove(event) -def sort_virtual_continent(event_list) -> List[Event]: - # Return 2D list of events separated in virtual and by continent. - # Index Key: [[0=Virtual], [1=Africa], [2=Asia], - # [3=Europe], [4=North America], - # [5=Oceania], [6=South America]] - separated_event_list = [[], [], [], [], [], []] +def group_virtual_continent(event_list): + # Return dictionary of events separated in virtual and by continent. + separated_event_list = {} for event in event_list: # Separates Events by Virtual or by Continent - if event.virtual: - separated_event_list[0].append(event) - else: - continent = country_code_to_continent(event.location[-2:]) - if continent == "Africa": - separated_event_list[1].append(event) - elif continent == "Asia": - separated_event_list[2].append(event) - elif continent == "Europe": - separated_event_list[3].append(event) - elif continent == "North America": - separated_event_list[4].append(event) - elif continent == "Oceania": - separated_event_list[5].append(event) - elif continent == "South America": - separated_event_list[6].append(event) - + key = "Virtual" if event.virtual else country_code_to_continent(event.location[-2:]) + separated_event_list.setdefault(key, []).append(event) + return separated_event_list diff --git a/tools/events-automation/state_territory_to_abbrev.py b/tools/events-automation/state_territory_to_abbrev.py index 77f9b6a29..0d74642f8 100644 --- a/tools/events-automation/state_territory_to_abbrev.py +++ b/tools/events-automation/state_territory_to_abbrev.py @@ -1,102 +1,102 @@ -# Returns the abbreviated version of state/territory name for AU, CA, and US. -# Information from Wikipedia. - -US_STATE_TO_ABBREV = { - "Alabama": "AL", - "Alaska": "AK", - "Arizona": "AZ", - "Arkansas": "AR", - "California": "CA", - "Colorado": "CO", - "Connecticut": "CT", - "Delaware": "DE", - "Florida": "FL", - "Georgia": "GA", - "Hawaii": "HI", - "Idaho": "ID", - "Illinois": "IL", - "Indiana": "IN", - "Iowa": "IA", - "Kansas": "KS", - "Kentucky": "KY", - "Louisiana": "LA", - "Maine": "ME", - "Maryland": "MD", - "Massachusetts": "MA", - "Michigan": "MI", - "Minnesota": "MN", - "Mississippi": "MS", - "Missouri": "MO", - "Montana": "MT", - "Nebraska": "NE", - "Nevada": "NV", - "New Hampshire": "NH", - "New Jersey": "NJ", - "New Mexico": "NM", - "New York": "NY", - "North Carolina": "NC", - "North Dakota": "ND", - "Ohio": "OH", - "Oklahoma": "OK", - "Oregon": "OR", - "Pennsylvania": "PA", - "Rhode Island": "RI", - "South Carolina": "SC", - "South Dakota": "SD", - "Tennessee": "TN", - "Texas": "TX", - "Utah": "UT", - "Vermont": "VT", - "Virginia": "VA", - "Washington": "WA", - "West Virginia": "WV", - "Wisconsin": "WI", - "Wyoming": "WY", - "District of Columbia": "DC", - "American Samoa": "AS", - "Guam": "GU", - "Northern Mariana Islands": "MP", - "Puerto Rico": "PR", - "United States Minor Outlying Islands": "UM", - "U.S. Virgin Islands": "VI", -} - -CA_STATE_TERRITORY_TO_ABBREV = { - "Alberta": "AB", - "British Columbia": "BC", - "Manitoba": "MB", - "New Brunswick": "NB", - "Newfoundland and Labrador": "NL", - "Northwest Territories": "NT", - "Nova Scotia": "NS", - "Nunavut": "NU", - "Ontario": "ON", - "Prince Edward Island": "PE", - "Quebec": "QC", - "Saskatchewan": "SK", - "Yukon": "YT", -} - -AU_STATE_TERRITORY_TO_ABBREV = { - "New South Wales": "NSW", - "Northern Territory": "NT", - "Queensland": "QLD", - "South Australia": "SA", - "Tasmania": "TAS", - "Victoria": "VIC", - "Western Australia": "WA", -} - -def us_state_to_abbrev(state): - # Returns the abbreviated alpha code for input state. - return US_STATE_TO_ABBREV[state] - - -def ca_state_territory_to_abbrev(state): - # Returns the abbreviated alpha code for input state/territory. - return CA_STATE_TERRITORY_TO_ABBREV[state] - - -def au_state_territory_to_abbrev(state): - # Returns the abbreviated alpha code for input state/territory. - return AU_STATE_TERRITORY_TO_ABBREV[state] +# Returns the abbreviated version of state/territory name for AU, CA, and US. +# Information from Wikipedia. + +US_STATE_TO_ABBREV = { + "Alabama": "AL", + "Alaska": "AK", + "Arizona": "AZ", + "Arkansas": "AR", + "California": "CA", + "Colorado": "CO", + "Connecticut": "CT", + "Delaware": "DE", + "Florida": "FL", + "Georgia": "GA", + "Hawaii": "HI", + "Idaho": "ID", + "Illinois": "IL", + "Indiana": "IN", + "Iowa": "IA", + "Kansas": "KS", + "Kentucky": "KY", + "Louisiana": "LA", + "Maine": "ME", + "Maryland": "MD", + "Massachusetts": "MA", + "Michigan": "MI", + "Minnesota": "MN", + "Mississippi": "MS", + "Missouri": "MO", + "Montana": "MT", + "Nebraska": "NE", + "Nevada": "NV", + "New Hampshire": "NH", + "New Jersey": "NJ", + "New Mexico": "NM", + "New York": "NY", + "North Carolina": "NC", + "North Dakota": "ND", + "Ohio": "OH", + "Oklahoma": "OK", + "Oregon": "OR", + "Pennsylvania": "PA", + "Rhode Island": "RI", + "South Carolina": "SC", + "South Dakota": "SD", + "Tennessee": "TN", + "Texas": "TX", + "Utah": "UT", + "Vermont": "VT", + "Virginia": "VA", + "Washington": "WA", + "West Virginia": "WV", + "Wisconsin": "WI", + "Wyoming": "WY", + "District of Columbia": "DC", + "American Samoa": "AS", + "Guam": "GU", + "Northern Mariana Islands": "MP", + "Puerto Rico": "PR", + "United States Minor Outlying Islands": "UM", + "U.S. Virgin Islands": "VI", +} + +CA_STATE_TERRITORY_TO_ABBREV = { + "Alberta": "AB", + "British Columbia": "BC", + "Manitoba": "MB", + "New Brunswick": "NB", + "Newfoundland and Labrador": "NL", + "Northwest Territories": "NT", + "Nova Scotia": "NS", + "Nunavut": "NU", + "Ontario": "ON", + "Prince Edward Island": "PE", + "Quebec": "QC", + "Saskatchewan": "SK", + "Yukon": "YT", +} + +AU_STATE_TERRITORY_TO_ABBREV = { + "New South Wales": "NSW", + "Northern Territory": "NT", + "Queensland": "QLD", + "South Australia": "SA", + "Tasmania": "TAS", + "Victoria": "VIC", + "Western Australia": "WA", +} + +def us_state_to_abbrev(state): + # Returns the abbreviated alpha code for input state. + return US_STATE_TO_ABBREV[state] + + +def ca_state_territory_to_abbrev(state): + # Returns the abbreviated alpha code for input state/territory. + return CA_STATE_TERRITORY_TO_ABBREV[state] + + +def au_state_territory_to_abbrev(state): + # Returns the abbreviated alpha code for input state/territory. + return AU_STATE_TERRITORY_TO_ABBREV[state] From db042755e798b25c65918f98a7dcc0d21acfb458 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell Date: Tue, 28 May 2024 21:27:03 -0700 Subject: [PATCH 26/32] Updates README.md. Implements some of the feedback given to improve documentation. --- tools/events-automation/README.md | 144 +++++++++++++++--------------- 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/tools/events-automation/README.md b/tools/events-automation/README.md index adbfff549..baa4a3441 100644 --- a/tools/events-automation/README.md +++ b/tools/events-automation/README.md @@ -1,71 +1,73 @@ -### Event Class: -**Required Variables** -- name (string): Title of the event. -- location (string): Location of the event either in full detail (e.g. `"111 test st, city, country, postcode"`) to be formatted by [geopy](https://geopy.readthedocs.io/en/stable/#) module and `format_location()` in event sink, or in `"city, state/territory, country"` format with state/territory details included if the location is in Australia, Canada or United States. See code sample included below for an example of location formatting. - - Note: If location string is in `"city, state, country"` format, for both state/territory and country ISO alpha-2 codes must be used (e.g. AU for Australia, CA for California). -- date (date or datetime): Date of event in the locations local time (NOT the local time of where the program is being run). -- url (string): Unique URL for event page details. -- virtual (boolean): If event is online. -- organizerName (string): Name of event organiser. -- organizerUrl (string): URL for event organiser webpage. - -**Additional Variable(s) for Internal Use:** -- duplicate (boolean): Flag for potential event duplicate based on evaluation during event sink. - -**Code Sample for Creating an Event:** -```py -Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url="website3.com", virtual=True, organizerName="Test Organizer", organizerUrl="testorg.com") -``` - -### Event Sink: -**Within Scope**: -- The event sink will take a list of event objects (see `test_events.py` for example data), format the date and location data (if not done already), filter out events that are outside of the pre-determined 'date window' for the current TWiR issue then sort the events by date and then location alphabetically. After this process the list is then split via virtual or continent, and any potential duplicate events within the list are flagged (through comparison of event data). Finally, the event sink will output the details of the finalised list of events in a pre-determined markdown format, complete with virtual/continent headers. -- Note that potential duplicate events will be flagged with a `** NOTE POTENTIAL DUPLICATE: **` warning immediately preceding the event information. - -**Out of Scope**: -- The purpose of the event sink is to cross-reference and curate data from various sources. It shouldn't be responsible for gathering or adding required fields of data into the Event class. Any edge cases should be managed by the event sources. - -### How to Add a New Event Source Module: -- To write a new event source module, it must be written in python. Event sources should be a function that passes no parameters and returns a list of events with required variables detailed above. If the event source has additional requirements, that should be added to `requirements.txt`. The event source should detail any specific run instructions in it's own documentation. Look at `test_events.py` for bare-minimum event list output. -- To run a new event source, import the new module from new event source and add the function call to `event_list`. - -### Requirements to run this code: -- Event sink requires Python installation. -- For specific module requirements: `pip install -r requirements.txt` - -### How to use this code: -- In order to use the Event Sink code please ensure that all requirements listed above have been met/installed on your device. -- Check that all Event Source module function calls are included in `event_list` (function calls should concatenate into a single list of event objects). - -### Architecture of the Event Sink: -- `main()`: Gathers a list of events from imported Event Source modules, formats the event date and location data, identifies a start date for the date-window filter, creates a 2D sorted list of events from `sort_and_filter_events()`, outputs to screen Event details in markdown format with section headers and duplicate warnings. -- `format_data(event_list)`: Formats event date and location data into pre-determined format (as described above) using Event Class functions `format_date()` and `format_location()`. - - `format_date()`: Identifies if date variable is a datetime object and if so, converts to date object. - - `format_location()`: Identifies if location data is already formatted. If not, parses location data through geopy module, extracts the required information, then formats the data. -- `sort_and_filter_events(event_list, start_date)`: Removes all events that are outside of the determined date window (start_date + 5 weeks), sorts the list of events first by date then by location, identifies potential duplicate events to be manually checked on completion, separates events by virtual/continent into a 2D list that is returned to `main()`. -- `sort_events(event_list)`: Uses an insertion sort algorithm to sort the list of events by date, then by location. -- `potential_duplicate(event_list)`: Flags potential duplicate events by comparing date, url, name, and organizerName variables (in that order). -- `sort_virtual_continent(event_list)`: Separates the event list into a 2D list by virtual/continent. See function code comments for index key. - -### Expected Output: -Example Output from `test_events.py` data: -``` -### Virtual: - -* 2024-04-12 | Virtual (Dublin, IE) | [Test Organizer](testorg.com) - *[**Test Event 1**](website1.com) - -### North America: - -* 2024-04-03 | New York, NY, US | [Test Organizer](testorg.com) - *[**Test Event 7**](website7.com) -* 2024-04-04 | San Francisco, CA, US | [Test Organizer](testorg.com) - *[**Test Event 6**](website6.com) -* 2024-04-18 | Indianapolis, IN, US | [Test Organizer](testorg.com) - *[**Test Event 2**](website2.com) - -### Oceania: - -* 2024-04-04 | Sydney, NSW, AU | [Test Organizer](testorg.com) - *[**Test Event 4**](website4.com) -``` +# Event Sink +The maintainer of the Events section is faced with manual work; this process automates generation of a draft events list using various Event Source Modules that gather event information, and an Event Sink that sorts, groups and formats the events into a pre-determined markdown format. + +## Getting Started: +### Pre-Requisites: +- Event sink requires Python3 installation. +- For specific module requirements: `pip install -r requirements.txt` +- See https://geopy.readthedocs.io/en/stable/# for `geopy` module documentation. + +### Running: +Before running please check that all Event Source module function calls are included in `event_list` (function calls should concatenate into a single list of event objects). + +To run this code: + +```py +pip install -r requirements.txt +python3 main.py +``` + +### How to Add a New Event Source Module: +- To write a new event source module, it must be written in python. Event sources should be a function that passes no parameters and returns a list of events with required variables detailed above. If the event source has additional requirements, that should be added to `requirements.txt`. The event source should detail any specific run instructions in it's own documentation. Look at `test_events.py` for bare-minimum event list output. +- To run a new event source, import the new module from new event source and add the function call to `event_list`. + +## Architecture: +### Event Class: +**Required Variables** +- `name` (string): Title of the event. +- `location` (string): Location of the event either in full detail (e.g. `"111 test st, city, country, postcode"`) to be formatted by `geopy` module and `format_location()` in event sink, or in `"city, state/territory, country"` format with state/territory details included if the location is in Australia, Canada or United States. See code sample included below for an example of location formatting. + - Note: If location string is in `"city, state, country"` format, for both state/territory and country ISO alpha-2 codes must be used (e.g. AU for Australia, CA for California). +- `date` (date or datetime): Date of event in the locations local time (NOT the local time of where the program is being run). +- `url` (string): Unique URL for event page details. +- `virtual` (boolean): If event is online. +- `organizerName` (string): Name of event organiser. +- `organizerUrl` (string): URL for event organiser webpage. + +**Additional Variable(s) for Internal Use:** +- `duplicate` (boolean): Flag for potential event duplicate based on evaluation during event sink. + +**Code Sample for Creating an Event:** +```py +Event(name="Test Event", location="Melbourne, VIC, AU", date=date.today(), url="website3.com", virtual=True, organizerName="Test Organizer", organizerUrl="testorg.com") +``` + +### Within Scope of Event Sink: +- The event sink will take a list of event objects (see `test_events.py` for example data), format the date and location data (if not done already), filter out events that are outside of the pre-determined 'date window' for the current TWiR issue then sort the events by date and then location alphabetically. After this process the list is then split via virtual or continent, and any potential duplicate events within the list are flagged (through comparison of event data). Finally, the event sink will output the details of the finalised list of events in a pre-determined markdown format, complete with virtual/continent headers. +- Note that potential duplicate events will be flagged with a `** NOTE POTENTIAL DUPLICATE: **` warning immediately preceding the event information. + + +### Out of Scope: +- The purpose of the event sink is to cross-reference and curate data from various sources. It shouldn't be responsible for gathering or adding required fields of data into the Event class. Any edge cases should be managed by the event sources. + +### Expected Output: +Example Output from `test_events.py` data: +``` +### Virtual: + +* 2024-04-12 | Virtual (Dublin, IE) | [Test Organizer](testorg.com) + *[**Test Event 1**](website1.com) + +### North America: + +* 2024-04-03 | New York, NY, US | [Test Organizer](testorg.com) + *[**Test Event 7**](website7.com) +* 2024-04-04 | San Francisco, CA, US | [Test Organizer](testorg.com) + *[**Test Event 6**](website6.com) +* 2024-04-18 | Indianapolis, IN, US | [Test Organizer](testorg.com) + *[**Test Event 2**](website2.com) + +### Oceania: + +* 2024-04-04 | Sydney, NSW, AU | [Test Organizer](testorg.com) + *[**Test Event 4**](website4.com) +``` From 7f8c31a1e4dd7428bd994d71c6f1d42deb12ff3d Mon Sep 17 00:00:00 2001 From: Hannah Cotterell Date: Mon, 3 Jun 2024 20:59:42 -0700 Subject: [PATCH 27/32] Updates main.py and event.py. Fixes timeout problem and "no city data" bug with geopy, adds location check to potential_duplicate() function. --- tools/events-automation/event.py | 4 ++-- tools/events-automation/main.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index e2f62a9f2..5f8ed314a 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -26,10 +26,10 @@ def format_date(self): def format_location(self): # Formats location data into (city, +/-state, country). - geocoder = Nominatim(user_agent="TWiR") + geocoder = Nominatim(user_agent="TWiR", timeout=5) locationData = geocoder.geocode(self.location, language="en", addressdetails=True).raw["address"] - country_code, city = locationData["country_code"].upper(), locationData["city"] + country_code, city = locationData["country_code"].upper(), locationData.get("city", locationData.get("town", locationData.get("village", "**NO CITY DATA**"))) if country_code in ["AU", "CA", "US"]: state = locationData["state"] if country_code == "AU": diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index 940d6c6a6..6acd9853e 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -87,7 +87,8 @@ def potential_duplicate(event_list): if event_list[i].url == event_list[j].url: if event_list[i].name == event_list[j].name: if event_list[i].organizerName == event_list[j].organizerName: - event_list[i].duplicate = True + if event_list[i].location == event_list[j].location: + event_list[i].duplicate = True if __name__ == "__main__": From 0020b3001893c618ec77d7b348ff87f9428c698e Mon Sep 17 00:00:00 2001 From: Hannah Cotterell Date: Mon, 3 Jun 2024 21:05:01 -0700 Subject: [PATCH 28/32] Updates code comments for more accurate description of functionality. --- tools/events-automation/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index 6acd9853e..c0674f6df 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -25,7 +25,7 @@ def main(): # Flag potential duplicate events. potential_duplicate(event_list) - # Sort into virtual or by continent. + # Group by virtual or by continent. event_list = group_virtual_continent(event_list) # Output Sorted Event List. From 4dc71cc3d5e17857bde22c05ac0078cc8b24f396 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell Date: Mon, 3 Jun 2024 21:26:41 -0700 Subject: [PATCH 29/32] Fixes bug where state data not available for formatting location. --- tools/events-automation/event.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/events-automation/event.py b/tools/events-automation/event.py index 5f8ed314a..3af3037dd 100644 --- a/tools/events-automation/event.py +++ b/tools/events-automation/event.py @@ -31,8 +31,10 @@ def format_location(self): country_code, city = locationData["country_code"].upper(), locationData.get("city", locationData.get("town", locationData.get("village", "**NO CITY DATA**"))) if country_code in ["AU", "CA", "US"]: - state = locationData["state"] - if country_code == "AU": + state = locationData.get("state", locationData.get("territory", "**NO STATE DATA**")) + if state == "**NO STATE DATA**": + state_abbrev = state + elif country_code == "AU": state_abbrev = au_state_territory_to_abbrev(state) elif country_code == "CA": state_abbrev = ca_state_territory_to_abbrev(state) From 4fc58e57478fa79d00b6a727b1b0b4bc38511627 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell Date: Mon, 3 Jun 2024 22:01:29 -0700 Subject: [PATCH 30/32] Updates state_territory_to_abbrev.py to include the Australian Capital Territory. --- tools/events-automation/state_territory_to_abbrev.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/events-automation/state_territory_to_abbrev.py b/tools/events-automation/state_territory_to_abbrev.py index 0d74642f8..2982d2cbd 100644 --- a/tools/events-automation/state_territory_to_abbrev.py +++ b/tools/events-automation/state_territory_to_abbrev.py @@ -78,6 +78,7 @@ } AU_STATE_TERRITORY_TO_ABBREV = { + "Australian Capital Territory": "ACT", "New South Wales": "NSW", "Northern Territory": "NT", "Queensland": "QLD", From 44f080cabea2ff1604c057d84e8607abe792b934 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell Date: Mon, 3 Jun 2024 23:04:13 -0700 Subject: [PATCH 31/32] Updates main.py. Fixes problem with output so that event_list is output Virtual first, then Continents in alphabetical order. --- tools/events-automation/main.py | 45 +++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/tools/events-automation/main.py b/tools/events-automation/main.py index c0674f6df..fde3ceb6d 100644 --- a/tools/events-automation/main.py +++ b/tools/events-automation/main.py @@ -33,19 +33,26 @@ def main(): def output_to_screen(event_list): - # Prints sorted Event List to terminal screen. - for key, value in event_list.items(): - if len(value) == 0: - continue - else: - print(f'### {key}:\n') - - # Output event details - for event in value: - if event.duplicate: - print("** NOTE POTENTIAL DUPLICATE: **") - print(event.to_markdown_string()) - print() + # Outputs sorted Event List to terminal screen. + # Output Virtual Events: + if "Virtual" in event_list: + print("### Virtual:\n") + output_event_details(event_list["Virtual"]) + del event_list["Virtual"] + + # Output Non-Virtual Events: + for key, value in dict(sorted(event_list.items())).items(): + print(f'### {key}:\n') + output_event_details(value) + + +def output_event_details(event_group): + # Outputs event details + for event in event_group: + if event.duplicate: + print("** NOTE POTENTIAL DUPLICATE: **") + print(event.to_markdown_string()) + print() def format_data(event_list): @@ -83,12 +90,12 @@ def potential_duplicate(event_list): # Identifies possible duplicate Events within Event List. for i in range(len(event_list)): for j in range(i+1, len(event_list)): - if event_list[i].date == event_list[j].date: - if event_list[i].url == event_list[j].url: - if event_list[i].name == event_list[j].name: - if event_list[i].organizerName == event_list[j].organizerName: - if event_list[i].location == event_list[j].location: - event_list[i].duplicate = True + if event_list[i].date == event_list[j].date and \ + event_list[i].url == event_list[j].url and \ + event_list[i].name == event_list[j].name and \ + event_list[i].organizerName == event_list[j].organizerName and \ + event_list[i].location == event_list[j].location: + event_list[i].duplicate = True if __name__ == "__main__": From 7bc9a8ee41934ad4555bc40405676728f8a1fae6 Mon Sep 17 00:00:00 2001 From: Hannah Cotterell <143161783+HanCotterell@users.noreply.github.com> Date: Tue, 9 Jul 2024 21:23:35 -0700 Subject: [PATCH 32/32] Update state_territory_to_abbrev.py Added option for NO STATE DATA variables --- tools/events-automation/state_territory_to_abbrev.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/events-automation/state_territory_to_abbrev.py b/tools/events-automation/state_territory_to_abbrev.py index 2982d2cbd..3437a3508 100644 --- a/tools/events-automation/state_territory_to_abbrev.py +++ b/tools/events-automation/state_territory_to_abbrev.py @@ -59,6 +59,7 @@ "Puerto Rico": "PR", "United States Minor Outlying Islands": "UM", "U.S. Virgin Islands": "VI", + "**NO STATE DATA**": "**NO STATE DATA**", } CA_STATE_TERRITORY_TO_ABBREV = { @@ -75,6 +76,7 @@ "Quebec": "QC", "Saskatchewan": "SK", "Yukon": "YT", + "**NO STATE DATA**": "**NO STATE DATA**", } AU_STATE_TERRITORY_TO_ABBREV = { @@ -86,6 +88,7 @@ "Tasmania": "TAS", "Victoria": "VIC", "Western Australia": "WA", + "**NO STATE DATA**": "**NO STATE DATA**", } def us_state_to_abbrev(state):