Last active
April 24, 2023 15:44
-
-
Save jordigg/589fe96ea6b513d0aae13c3fed73cfca to your computer and use it in GitHub Desktop.
Generates a calendar file in ICS format with holidays around the world that are available on the nager API. It groups similar holidays showing the flag emoji of the corresponding countries. It also tries to find a Wikipedia link with more information about the holiday.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import wikipedia | |
import requests | |
from datetime import datetime | |
from ics import Calendar, Event | |
import argparse | |
import warnings | |
from bs4 import GuessedAtParserWarning | |
# Parses command line arguments | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--verbose', action='store_true', | |
help='Enable verbose mode') | |
parser.add_argument('--quick', action='store_true', | |
help='Enable quick mode (skip Wikipedia page search)') | |
parser.add_argument('--year', type=int, | |
help='Year to generate holidays for (default: current year)', default=datetime.now().year) | |
args = parser.parse_args() | |
verbose = args.verbose | |
quick = args.quick | |
year = args.year | |
# Ignore warnings | |
# Ignore warning from wikipedia API | |
warnings.filterwarnings("ignore", category=GuessedAtParserWarning) | |
def vprint(text): | |
if verbose: | |
print(text) | |
def get_country_codes(): | |
response = requests.get( | |
"https://restcountries.com/v3.1/all?fields=cca2,name") | |
rest_countries_data = response.json() | |
country_codes = { | |
entry["cca2"]: get_flag_emoji(entry["cca2"]) for entry in rest_countries_data | |
} | |
return country_codes | |
def get_flag_emoji(country_code): | |
flag_offset = 127397 | |
code_points = [ord(char) + flag_offset for char in country_code.upper()] | |
return chr(code_points[0]) + chr(code_points[1]) | |
def get_country_name(country_code, country_codes): | |
for country_data in rest_countries_data: | |
if country_data["cca2"] == country_code: | |
return country_data["name"]["common"] | |
return "" | |
def get_wikipedia_url(search_term, quick): | |
if quick: | |
return f"https://en.wikipedia.org/wiki/Special:Search?search={search_term.replace(' ', '+')}" | |
try: | |
search_results = wikipedia.search(search_term) | |
for result in search_results: | |
try: | |
wiki_page = wikipedia.page(result) | |
return wiki_page.url | |
except wikipedia.exceptions.DisambiguationError as e: | |
if len(e.options) > 0: | |
wiki_page = wikipedia.page(e.options[0]) | |
return wiki_page.url | |
except wikipedia.exceptions.PageError: | |
continue | |
except Exception as e: | |
print(f"Error fetching Wikipedia page for {search_term}: {e}") | |
return f"https://en.wikipedia.org/wiki/{search_term.replace(' ', '_')}" | |
def camelized_name(name): | |
return ' '.join(word.capitalize() for word in name.split()) | |
def fetch_holidays(country_codes, year): | |
url = f"https://date.nager.at/api/v3/PublicHolidays/{year}/" | |
calendar = Calendar() | |
holiday_dict = {} | |
total_countries = len(country_codes) | |
processed_countries = 0 | |
total_holidays = len(holiday_dict.items()) | |
processed_holidays = 0 | |
for country_code, flag in country_codes.items(): | |
processed_countries += 1 | |
progress = (processed_countries / total_countries) * 100 | |
vprint( | |
f"Fetching holidays for {country_code} - Progress: {progress:.2f}%") | |
response = requests.get(url + country_code) | |
if response.status_code != 200: | |
continue | |
holidays = response.json() | |
country_name = get_country_name(country_code, country_codes) | |
for holiday in holidays: | |
holiday_date = datetime.strptime( | |
holiday["date"], "%Y-%m-%d").date() | |
holiday_name = camelized_name(holiday["name"]) | |
holiday_key = (holiday_name, holiday_date) | |
if holiday_key in holiday_dict: | |
holiday_dict[holiday_key]["flags"].append(flag) | |
holiday_dict[holiday_key]["countries"].append(country_name) | |
else: | |
holiday_dict[holiday_key] = { | |
"flags": [flag], "countries": [country_name]} | |
for (holiday_name, holiday_date), holiday_data in holiday_dict.items(): | |
event = Event() | |
event.name = f"{holiday_name} {''.join(holiday_data['flags'])}" | |
event.begin = holiday_date | |
event.make_all_day() | |
if len(holiday_data["countries"]) == 1: | |
event.location = holiday_data["countries"][0] | |
search_term = f"{holiday_name} {holiday_data['countries'][0]}" | |
else: | |
search_term = holiday_name | |
event.description = f"{holiday_name} - {'/'.join(holiday_data['countries'])}\nWikipedia: {get_wikipedia_url(search_term, quick)}" | |
calendar.events.add(event) | |
processed_holidays += 1 | |
total_holidays = len(holiday_dict.items()) | |
vprint( | |
f"Processing holiday {processed_holidays}/{total_holidays}: {holiday_name}") | |
return calendar | |
# Fetch rest countries data | |
response = requests.get("https://restcountries.com/v3.1/all?fields=cca2,name") | |
rest_countries_data = response.json() | |
# Fetch country codes and generate calendar | |
country_codes = get_country_codes() | |
calendar = fetch_holidays(country_codes, year) | |
# Write calendar to file | |
with open(f"{year}_world_holidays.ics", "w") as file: | |
file.writelines(calendar) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment