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.
Is your system ready?
If you already have the Nylas Python SDK installed and your Python environment configured, skip to the next section.
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.
Installing the required packages
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.
Coding our Reflex Contact Manager
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: