"""Get the current Python events from an ics file and format them into a markdown string."""from__future__importannotationsfromreimportsearchfromtypingimportAnyimportanyioimportarrowfromhttpximportAsyncClient,codesfromicalendarimportCalendar__all__=("FetchFailedError","fetch_ics","format_event","main","parse_ics_to_md","write_to_md_file")
[docs]classFetchFailedError(Exception):"""Raised when the fetch fails for any reason."""
[docs]asyncdeffetch_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 """asyncwithAsyncClient(follow_redirects=True)asclient:resp=awaitclient.get(url)ifresp.status_code!=codes.OK:msg=f"Status code: {resp.status_code}"raiseFetchFailedError(msg)returnresp.text
[docs]defformat_event(event_time_start:arrow.Arrow,event_time_end:arrow.Arrow|None,summary:str,link:str)->str:"""Formats the event into a markdown string. Args: event_time_start (arrow.Arrow): The start time of the event event_time_end (arrow.Arrow): The end time of the event summary (str): The summary of the event link (str): The link to the event Returns: str: The markdown string """start_date_str=event_time_start.format("D MMM")end_date_str=event_time_end.format("D MMM")ifevent_time_endelse""date_range=f"{start_date_str} - {end_date_str}"ifend_date_strelsestart_date_strreturnf"* [{summary}]({link}) {date_range}\n"
[docs]defparse_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=[]forcomponentincal.walk():ifcomponent.name=="VEVENT":event_time_start=arrow.get(component.get("dtstart").dt)event_time_end=arrow.get(component.get("dtend").dt)ifcomponent.get("dtend")elseNonedescription=component.get("description")summary=component.get("summary")search_result=search(r'(?P<url>https?://[^\s">]+)',description)ifdescriptionelseNonelink=search_result.group("url")ifsearch_resultelse""event_md=format_event(event_time_start,event_time_end,summary,link)ifnow<=event_time_start<next_month:future_events.append(event_md)elifnow>=event_time_startand(event_time_endisNoneornow<=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)returnf"{full_calendar_link}### Current Events\n{current_events_str}\n### Future Events\n{future_events_str}"
[docs]asyncdefwrite_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 """asyncwithawaitanyio.open_file(file_path,"w")asf:awaitf.write(content)
[docs]asyncdefmain()->None:"""Make events go brr."""ics_url=("https://www.google.com/calendar/ical/j7gov1cmnqr9tvg14k621j7t5c@group.calendar.google.com/public/basic.ics")ics_str=awaitfetch_ics(ics_url)events_md=parse_ics_to_md(ics_str)awaitwrite_to_md_file(events_md,"events.md")