- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
Ever get an invitation to a hackathon, a party or (yikes) a job interview, only to find it weeks or months later because it went to the wrong email or voicemail box? If you rely on an old email or cellphone number, this could happen to you! It’s critical to keep your contacts up to date so you don’t miss opportunities. How about we use Nylas to build our interface for managing contact info? We’re going to make a Contact Manager to update our contacts.
To build our Contact Manager, we’re going to use Reflex, a Python-based Web-UI Framework.
If you already have the Nylas Python SDK installed and your Python environment configured, skip to the next section.
If not, read the post How to Send Emails with the Nylas Python SDK, where I cover the basic steps to set up your environment.
When you run your Reflex Contact Manager, it will display five contacts that belong to a group that you specify. Of course, with Nylas you can display all contacts without worrying if they belong to a group or not. It’s your choice:
Next to each contact, we’ll add an update button that shows all the contact detail fields.
To update a contact, edit any of the fields and click Submit to save the change.
After that, the contact shows the change when you view it again.
One important limitation to mention – I haven’t found a way to enable notifications when the contact gets updated, but we can see that it was because the updated job title shows up in the contact card.
Besides the Nylas and Dotenv packages, we need to install the Reflex and the Pandas packages:
$ pip3 install reflex $ pip3 install pandas
That’s it! Nothing too complex or complicated.
And now it’s coding time. Go to your terminal window and type the following:
$ mkdir reflex_contacts_manager $ cd reflex_contacts_manager $ pc init
This creates some files and libraries, but importantly it creates a new folder called reflex_contacts_manager. Inside this folder, you’ll find the file reflex_contacts_mananer.py, which we need to update. The code in this file is big, so we’ll split our discussion into more digestible sections:
In this section, we load all libraries and fetch the list of contacts:
# Import your dependencies from rxconfig import config import reflex as rx from dotenv import load_dotenv import os from nylas import APIClient # type: ignore import pandas as pd # Load the app configuration filename = f"{config.app_name}/{config.app_name}.py" # 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"), ) # Get a list of contacts, from a particular group # limiting to 5 contacts only def get_contacts(): ids = [] full_names = [] # Call the contacts endpoint contacts = nylas.contacts.where(source = 'address_book', limit = 5, group = "517v55haghlcvnuu7lcm4f7k8") # Loop the contacts for contact in contacts: # Append them to lists ids.append(contact.id) full_names.append(contact.given_name + " " + contact.surname) # Create a dictionary data = {'ids' : ids, 'full_names' : full_names} # Create a Pandas Dataframe df = pd.DataFrame(data) return df
This section handles all the application variables, gets the details for each contact, binds the changes on each field and handles the contact updates:
# This class will handle the list of contacts class Contact(rx.Model, table = True): ids : str full_names : str def __init__(self, ids, full_names): self.ids = ids self.full_names = full_names # This handles all the variables in our application class State(rx.State): form_data: dict = {} contact_id : str = "" contact_name : str = "" contact_surname : str = "" contact_company : str = "" contact_jobtitle : str = "" contact_email : str = "" contact_profilepic : str = "" contact_emailtype : str = "" contact_country : str = "" contact_street : str = "" contact_city : str = "" contact_state : str = "" contact_postal_code : str = "" contact_phone_type : str = "" contact_address_type : str = "" contact_phone_number : str = "" # Fetch all contacts _contacts = get_contacts() # Create a contacts list contacts:list[Contact] = [] for i in range(0, len(_contacts)): # Fill up the list contacts.append(Contact(_contacts.iloc[i]['ids'], _contacts.iloc[i]['full_names'])) # Get details for each selected contact def get_contact_details(self, contact_id : str): contact = nylas.contacts.get(contact_id) file_name = f'{contact_id}.png' # Download the contact picture picture = contact.get_picture() profile_image = open(f'assets/{file_name}', 'wb') profile_image.write(picture.read()) profile_image.close() # Store contact details on local variables self.contact_id = contact_id self.contact_profilepic = file_name self.contact_name = contact.given_name self.contact_surname = contact.surname self.contact_company = contact.company_name self.contact_jobtitle = contact.job_title if contact.job_title != None else "Empty" self.contact_email = list(contact.emails.values())[0][0] try: self.contact_phone_number = list(contact.phone_numbers.values())[0][0] self.contact_phone_type = list(contact.phone_numbers)[0] except Exception as e: print(f'{e}') self.contact_phone_number = "123" self.contact_phone_type = "Mobile" try: self.contact_address_type = list(contact.physical_addresses.values())[0][0]["type"] self.contact_country = list(contact.physical_addresses.values())[0][0]["country"] self.contact_street = list(contact.physical_addresses.values())[0][0]["street_address"] self.contact_city = list(contact.physical_addresses.values())[0][0]["city"] self.contact_state = list(contact.physical_addresses.values())[0][0]["state"] self.contact_postal_code = list(contact.physical_addresses.values())[0][0]["postal_code"] except Exception as e: print(f'{e}') self.contact_address_type = "" self.contact_country = "" self.contact_street = "" self.contact_city = "" self.contact_state = "" self.contact_postal_code = "" # These functions help us bind # the updated value with the UI def set_name(self, name: str): self.contact_name = name def set_surname(self, surname: str): self.contact_surname = surname def set_company(self, company: str): self.contact_company = company def set_jobtitle(self, jobtitle: str): self.contact_jobtitle = jobtitle def set_email(self, email: str): self.contact_email = email def set_country(self, country: str): self.contact_country = country def set_street(self, street: str): self.contact_street = street def set_city(self, city: str): self.contact_city = city def set_state(self, state: str): self.contact_state = state def set_postal_code(self, postal_code: str): self.contact_postal_code = postal_code def set_phone_number(self, phone_number: str): self.contact_phone_number = phone_number # When we press Submit def handle_submit(self, form_data: dict): # form_data is a dictionary that contains # all the form variables self.form_data = form_data # Update values if any self.form_data['id'] = self.contact_id self.form_data['contact_name'] = self.contact_name self.form_data['surname'] = self.contact_surname self.form_data['company_name'] = self.contact_company self.form_data['job_title'] = self.contact_jobtitle self.form_data['email'] = self.contact_email self.form_data['email_type'] = self.contact_emailtype self.form_data['phone_number'] = self.contact_phone_number self.form_data['country'] = self.contact_country self.form_data['street_address'] = self.contact_street self.form_data['city'] = self.contact_city self.form_data['state'] = self.contact_state self.form_data['postal_code'] = self.contact_postal_code self.form_data['phone_type'] = self.contact_phone_type self.form_data['address_type'] = self.contact_address_type contact = nylas.contacts.get(self.form_data['id']) contact.given_name = self.form_data['contact_name'] contact.surname = self.form_data['surname'] contact.company_name = self.form_data['company_name'] contact.job_title = self.form_data['job_title'] contact.emails[self.form_data['email_type']] = [self.form_data['email']] contact.phone_numbers[self.form_data['phone_type']] = [self.form_data['phone_number']] if self.form_data['street_address'] is not None or self.form_data['street_address'] != '': contact.physical_addresses['Work'] = [{ 'format': 'structured', 'city': self.form_data['city'], 'country': self.form_data['country'], 'state': self.form_data['state'], 'postal_code': self.form_data['postal_code'], 'type': self.form_data['address_type'], 'street_address': self.form_data['street_address']}] try: # Try to save the contact contact.save() except Exception as e: print(f'{e}')
The last section belongs to the UI of our application:
# This is our UI def index() -> rx.Component: return rx.center( rx.vstack( rx.vstack( rx.hstack( rx.heading("Contacts"), ), rx.flex( rx.box( rx.table_container( rx.table( rx.thead( rx.tr( rx.th("Names"), ) ), rx.tbody( rx.foreach(State.contacts, lambda contact: rx.tr( rx.td(contact.full_names), rx.td( # The update button next to each contact rx.button( "update", on_click = lambda: State.get_contact_details(contact.ids), bg = "red", color = "white", ) ) ) ) ) ) ), bg="#F7FAFC ", border="1px solid #ddd", border_radius="25px", ), # This form holds all the contact information # that we can update rx.form( rx.center( rx.image(src=State.contact_profilepic, width="100px", height="auto"), ), rx.hstack( rx.text("First Name:", as_ = "b"), rx.input( placeholder = State.contact_name, on_change = State.set_name, id = "contact_name", ), ), rx.hstack( rx.text("Last Name:", as_ = "b"), rx.input( placeholder = State.contact_surname, on_change = State.set_surname, id = "surname", ), ), rx.hstack( rx.text("Company Name:", as_ = "b"), rx.input( placeholder = State.contact_company, on_change = State.set_company, id = "company_name", ), ), rx.hstack( rx.text("Job Title:", as_ = "b"), rx.input( placeholder = State.contact_jobtitle, on_change = State.set_jobtitle, id = "job_title", ), ), rx.hstack( rx.text("Email:", as_ = "b"), rx.input( placeholder = State.contact_email, on_change = State.set_email, id = "email", ), ), rx.hstack( rx.text("Phone Number:", as_ = "b"), rx.input( placeholder = State.contact_phone_number, on_change = State.set_phone_number, id = "phone_number", ), ), rx.hstack( rx.text("Country:", as_ = "b"), rx.input( placeholder = State.contact_country, on_change = State.set_country, id = "country", ), ), rx.hstack( rx.text("Address:", as_ = "b"), rx.input( placeholder = State.contact_street, on_change = State.set_street, id = "street_address", ), ), rx.hstack( rx.text("City:", as_ = "b"), rx.input( placeholder = State.contact_city, on_change = State.set_city, id = "city", ), ), rx.hstack( rx.text("State:", as_ = "b"), rx.input( placeholder = State.contact_state, on_change = State.set_state, id = "state", ), ), rx.hstack( rx.text("Postal Code:", as_ = "b"), rx.input( placeholder = State.contact_postal_code, on_change = State.set_postal_code, id = "postal_code", ), ), rx.center( rx.hstack( rx.button( "Submit", type_="submit", bg = "red", color = "white", ), ), ), on_submit=State.handle_submit, ), bg="#F7FAFC ", border="1px solid #ddd", border_radius="25px", ) ) ) ) # Add state and page to the app. app = rx.App(state=State) app.add_page(index) app.compile()
To run our application, just type the following in your terminal window:
$ pc run
Our application runs on port 3000 of localhost, so open up your favourite browser and type:
http://localhost:3000/
We will be ready to use manage our contacts using Reflex.
If you want to learn more about our Contacts APIs, see our documentation Contacts API Overview.
You can sign up 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.