Skip to content

Instantly share code, notes, and snippets.

@jordigg
Last active April 24, 2023 15:44
Show Gist options
  • Save jordigg/589fe96ea6b513d0aae13c3fed73cfca to your computer and use it in GitHub Desktop.
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.
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