"""Get the current Python events from an ics file and format them into a markdown string."""
from __future__ import annotations
from re import search
from typing import Any
import anyio
import arrow
from httpx import AsyncClient, codes
from icalendar import Calendar
__all__ = ("FetchFailedError", "fetch_ics", "format_event", "main", "parse_ics_to_md", "write_to_md_file")
[docs]
class FetchFailedError(Exception):
"""Raised when the fetch fails for any reason."""
[docs]
async def fetch_ics(url: str) -> Any: # noqa: ANN401
"""Fetches the ics file from the given url and returns the text content.
Args:
url (str): The url to fetch the ics file from
Raises:
FetchFailedException: If the fetch fails for any reason
Returns:
str: The text content of the ics file
"""
async with AsyncClient(follow_redirects=True) as client:
resp = await client.get(url)
if resp.status_code != codes.OK:
msg = f"Status code: {resp.status_code}"
raise FetchFailedError(msg)
return resp.text
[docs]
def parse_ics_to_md(ics_str: str) -> str:
"""Parses the ics string into a markdown string.
Args:
ics_str (str): The ics string to parse
Returns:
str: The markdown string
"""
cal = Calendar.from_ical(ics_str)
now = arrow.now()
next_month = now.shift(months=1)
current_events = []
future_events = []
for component in cal.walk():
if component.name == "VEVENT":
event_time_start = arrow.get(component.get("dtstart").dt)
event_time_end = arrow.get(component.get("dtend").dt) if component.get("dtend") else None
description = component.get("description")
summary = component.get("summary")
search_result = search(r'(?P<url>https?://[^\s">]+)', description) if description else None
link = search_result.group("url") if search_result else ""
event_md = format_event(event_time_start, event_time_end, summary, link)
if now <= event_time_start < next_month:
future_events.append(event_md)
elif now >= event_time_start and (event_time_end is None or now <= event_time_end):
current_events.append(event_md)
full_calendar_link = "[Full Events Calendar](https://www.python.org/events/python-events)\n\n"
current_events_str = "".join(current_events)
future_events_str = "".join(future_events)
return f"{full_calendar_link}### Current Events\n{current_events_str}\n### Future Events\n{future_events_str}"
[docs]
async def write_to_md_file(content: str, file_path: str) -> None:
"""Writes the content to the given file path.
Args:
content (str): The content to write
file_path (str): The file path to write to
"""
async with await anyio.open_file(file_path, "w") as f:
await f.write(content)
[docs]
async def main() -> None:
"""Make events go brr."""
ics_url = (
"https://www.google.com/calendar/ical/j7gov1cmnqr9tvg14k621j7t5c@group.calendar.google.com/public/basic.ics"
)
ics_str = await fetch_ics(ics_url)
events_md = parse_ics_to_md(ics_str)
await write_to_md_file(events_md, "events.md")
if __name__ == "__main__":
anyio.run(main)