- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
In this tech world, scheduling meetings by agreeing on a common date and time is common. However, we don’t always have access to our calendars or we assume we have an available spot, which makes the meeting invite go back and forth until the meeting is finally decided. While here at Nylas we provide a powerful scheduler, it’s always a good thing to be able to create our version, and for that, we will use FastAPI, a powerful and blazing-fast Python framework. This application will read upcoming events from contacts using FastAPI.
If you already have the Nylas Python SDK installed and your Python environment configured, skip to the next section.
If this is your first time working with the Nylas SDK, I recommend reading the post How to Send Emails with the Nylas Python SDK, where I explain how to set up a basic environment.
When we run our application, we will we’re be presented with a list of our contacts. We need to choose one and then press submit:
With the selected contact, we will look for all the upcoming events where that contact is a participant:
Nothing will be displayed if we choose a contact that doesn’t have upcoming events. And by upcoming events, we mean events that haven’t happened yet. The ones that happened already will not be taken into consideration.
Unlike Flask, FastAPI doesn’t have a bundled server, so we need to install one. To make things easier, use the following requirement.txt file:
# # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile # annotated-types==0.6.0 # via pydantic anyio==3.7.1 # via # fastapi # starlette beautifulsoup4==4.12.2 # via -r requirements.in certifi==2023.7.22 # via requests charset-normalizer==3.3.0 # via requests click==8.1.7 # via uvicorn exceptiongroup==1.1.3 # via anyio fastapi==0.103.2 # via -r requirements.in h11==0.14.0 # via uvicorn idna==3.4 # via # anyio # requests nylas==5.14.1 # via -r requirements.in pendulum==2.1.2 # via -r requirements.in pydantic==2.4.2 # via fastapi pydantic-core==2.10.1 # via pydantic python-dateutil==2.8.2 # via pendulum python-dotenv==1.0.0 # via -r requirements.in pytzdata==2020.1 # via pendulum requests[security]==2.31.0 # via nylas six==1.16.0 # via # nylas # python-dateutil # websocket-client sniffio==1.3.0 # via anyio soupsieve==2.5 # via beautifulsoup4 starlette==0.27.0 # via fastapi typing-extensions==4.8.0 # via # fastapi # pydantic # pydantic-core # uvicorn urllib3==2.0.6 # via requests urlobject==2.4.3 # via nylas uvicorn==0.23.2 # via -r requirements.in websocket-client==0.59.0 # via nylas
Then run the following command on the terminal window:
$ pip3 install -r requirements.txt
This file was created using pipreqs:
$ pip3 install pipreqs $ pip3 install pip-tools $ pipreqs --savepath=requirements.in && pip-compile
We’re going to create a folder called Upcoming_Events_from_Contacts and inside create a folder called templates.
On the root folder, we’re going to create a file called upcoming_events.py with the following code:
# Import your dependencies from dotenv import load_dotenv import os import uvicorn from fastapi import FastAPI, Request, Form from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from typing import Annotated import pendulum from bs4 import BeautifulSoup from nylas import APIClient # type: ignore # Create a FastAPI application app = FastAPI() # Load your env variables load_dotenv() # Initialize an instance of the Nylas SDK using the client credentials nylas = APIClient( os.environ.get("CLIENT_ID"), os.environ.get("CLIENT_SECRET"), os.environ.get("ACCESS_TOKEN") ) # List to hold all contacts contact_list = [] # List to hold all events events_list = [] def get_contacts(): # Grab the first 5 contacts from the specified group contacts = nylas.contacts.where(source = 'address_book', group = "517v55haghlcvnuu7lcm4f7k8") # Loop all contacts and get the Email and Full Name for contact in contacts: contact_list.append({'email': list(contact.emails.values())[0][0] , 'full_name' : contact.given_name + " " + contact.surname}) def upcoming_events(email: str): # Get today’s date today = pendulum.now() # Not all events have a description description = "No description" # Get the time for today at the current time after_time = pendulum.local(today.year, today.month, today.day, today.hour, 0, 0).int_timestamp # Get all events where the contact is a participant events = nylas.events.where(calendar_id=os.environ.get("CALENDAR_ID"), starts_after=after_time, participants = email) # Remove all events from the list events_list.clear() # Loop the events for event in events: # If the event has a description if event.description is not None: # Get rid of all the html tags description = BeautifulSoup(event.description,features="html.parser").text # Determine the type of date event match event.when["object"]: case "timespan": start_date = pendulum.from_timestamp(event.when["start_time"], today.timezone.name).strftime("%m/%d/%Y at %H:%M") end_date = pendulum.from_timestamp(event.when["end_time"], today.timezone.name).strftime("%m/%d/%Y at %H:%M") event_date = f"from {start_date} to {end_date}" case "datespan": start_date = pendulum.from_timestamp(event.when["start_time"], today.timezone.name).strftime("%m/%d/%Y") end_date = pendulum.from_timestamp(event.when["end_time"], today.timezone.name).strftime("%m/%d/%Y") event_date = f"from {start_date} to {end_date}" case "date": event_date = pendulum.from_timestamp(event.when["date"], today.timezone.name).strftime("%m/%d/%Y") # Add the title, description and event date to the event events_list.append({'title': event.title, 'description': description, 'event_date': event_date}) # Return the list of events return events_list # Load the templates folder templates = Jinja2Templates(directory="templates") # Call the main page @app.get("/", response_class=HTMLResponse) async def get_events(request: Request): # Load contacts get_contacts() # Call the main.html template passing the list of contacts return templates.TemplateResponse("main.html", {"request": request, "contact_list": contact_list}) # When we press the submit button @app.post("/") # Read the form parameter async def post_events(request: Request, contacts: Annotated[str, Form()]): name = "" # Get a list of the upcoming events events_list = upcoming_events(contacts) # Loop the contacts for contact in contact_list: # Get the full name for the selected email if contact.get("email") == contacts: name = contact.get("full_name") exit # Call the main.html template passing the list of contacts, contact name and the list of events return templates.TemplateResponse("main.html", {"request": request, "contact_list": contact_list, "name":name, "events_list": events_list}) if __name__ == "__main__": # Use the uvicorn server to run the application uvicorn.run("upcoming_events:app")
Now, inside the templates, create the files base.html and main.html:
<!DOCTYPE html> <html> <head> <style> td { vertical-align: top; text-align: center } </style> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Call the TailwindCSS library --> <script src="https://cdn.tailwindcss.com"></script> <title>Upcoming Events from Contacts</title> </head> <body> {% block content %} {% endblock %} </body> </html>
We use Tailwind CSS to make our webpage look better:
{% extends 'base.html' %} {% block content %} <div class="bg-[#FFFFFF] border-b p-4 m-4 rounded grid place-items-center"> <form method="post"> <table> <tr> <td> <select name="contacts" id="contacts" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"> {% for contact in contact_list -%} <option value={{contact["email"]}}>{{contact["full_name"]}}</option> {% endfor %} </select> </td> <td > <button type="submit" class="text-white bg-gray-50 hover:bg-blue-800 focus:ring-4 focus:bg-gray-50 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Submit</button> </td> </tr> </table> </form> {% if name %} <h2 class="text-4xl font-bold dark:text-black">Upcoming events with {{name}}</h2><br> {% for events in events_list -%} <div class="bg-[#eeeeee] border-green-600 border-b p-4 m-4 rounded grid"> <h5 class="text-xl font-bold dark:text-black"><b>Title: </b>{{events["title"]}}</h5> <h5 class="text-xl font-bold dark:text-black"><b>Description: </b>{{events["description"]}}</h5> <h5 class="text-xl font-bold dark:text-black"><b>Date: </b>{{events["event_date"]}}</h5> </div> {% endfor %} {% endif %} {% endblock %}
Here, we show the contacts list combo box. If we have pressed submit yet, the variable name is empty, so we don’t display anything else.
When the variable name is not empty, we loop the events and display them on the screen.
The good thing about this script is that we don’t need anything special to run the server. We just need to type this on the terminal window:
$ python3 upcoming_events.py
Our application will be running on port 8000.
Now we can get all the upcoming events from our contacts using FastAPI.
You can check the Github repo here [Coming soon].
To learn more about our Contacts APIs, visit our documentation Contacts API Overview.
To learn more about our Calendar APIs, visit our documentation Calendar API Overview.
You can sign up for Nylas for free and start building!
Don’t miss the action, join our LiveStream Coding with Nylas!
Blag aka Alvaro Tejada Galindo is a Senior Developer Advocate at Nylas. He loves learning about programming and sharing knowledge with the community. When he’s not coding, he’s spending time with his wife, daughter and son. He loves Punk Music and reading all sorts of books.