Automaters Post
I have been trying to automate my Christmas card mailing for what seems like years now. I feel like I’m getting closer, but wanted to get some suggestions from the crowd. Here is what I got.
I already have a database of contacts on my phone/Mac via the contacts app so my goal was to utilize that. Since my contacts are set up as individuals and not families, a script was necessary to extract the data and format it properly.
I chose Python and Pythonista for its ability to access and manipulate the contacts database. I considered using the shortcuts app (for being less of an entry barrier), but I can’t access the relation’s names in shortcuts.
Here are the assumptions for my script:
- The tag “#xmas” goes in the notes section of contacts you want to send a card to
- Only apply the tag to the person you want to address first for multiple addressees, ie. having a spouse or partner. So for a husband and wife, customarily the husband is listed first. The spouse’s name will be listed in the related names.
- A name, address, and relatives are entered
- An address looks for a “home” or “mail” label, “mail” takes priority if entered
- Here are the addressing formats for contacts:
- Single: [Full Name]
ex. Johnny Appleseed - With Spouse: [Primary first name] & [Spouse first name] [Last name]
ex. Johnny & Mary Appleseed - With a partner: [Full Name] & [Partner]
A partner could be a girlfriend, boyfriend, or even a married person with a different last name. A partner is listed as “partner” in the related name section:
ex. Johnny Appleseed & Mary
ex. John Smith & Mary Cook
- Single: [Full Name]
- If Children are listed they are added below the main addressees. Children are labeled as “child” in the related names
ex. John Appleseed
and Joe, Mary
I am not a programmer, just a hobbyist. So the script could be improved, I’m sure.
Using the contacts database allows me to update addresses and relatives as they happen, then just run the script when needed.
The next problem is what to do with the output. Currently the output is mostly proof of concept as it isn’t able to be printed. I’m open to suggestions for how to get these to print to an envelope. Pages doesn’t have a native mail merge, but maybe it could be applied to a template somehow.
I considered outputting to HTML or LaTeX, which might be overkill.
I’ll share the script for those that want to play around.
import contacts
# Function definitions
def getMailAddress(address_dict):
street = address_dict['Street'].replace('/n','\n').strip()
city = address_dict['City']
state = address_dict['State']
zip = address_dict['ZIP']
country = ''
if address_dict[contacts.COUNTRY] != 'United States':
country = '\n' + address_dict[contacts.COUNTRY]
mail_to = '\n' + street + '\n' + city + ', ' + state + ' ' + zip + country
return mail_to
# Put all contacts in a variable
all_ppl = contacts.get_all_people()
# Loop through each person
for person in all_ppl:
# Reset defaults
contact_output = ''
children = ''
mail_to = ''
# Only deal with 'xmas' tagged people
if '#xmas' in person.note:
addresses = person.address
if addresses:
for address in addresses:
if address[0] == 'mail':
mail_to = getMailAddress(address[1])
break
elif address[0] == '_$!<Home>!$_':
mail_to = getMailAddress(address[1])
continue
else:
mail_to = '\n--No valid address--'
# Look for related names
# ie. have spouses, partner, kids
if person.related_names:
# Gather all children
children_list = [x[1] for x in person.related_names if x[0] == '_$!<Child>!$_']
# Gather all spouses
spouse_list = [x[1] for x in person.related_names if x[0] == '_$!<Spouse>!$_']
# Gather all girlfriend/boyfriend or significant others with a different last name which are labeled 'partner'
partner_list = [x[1] for x in person.related_names if x[0] == '_$!<Partner>!$_']
for relation_tuple in person.related_names:
# Make a child list if exists
if children_list:
children = ', '.join(children_list)
# Single with kids
if not spouse_list and not partner_list:
contact_output += person.full_name
if children_list:
contact_output += '\nand ' + children
contact_output += mail_to
print(contact_output)
print()
break
# Married with or without kids
elif spouse_list:
contact_output += person.first_name + ' & ' + ''.join(spouse_list) + ' ' + person.last_name
if children_list:
contact_output += '\nand ' + children
contact_output += mail_to
print(contact_output)
print()
break
# Has a significant other or different last name spouse with or without kids
elif partner_list:
contact_output += person.full_name + ' and ' + ''.join(partner_list)
if children_list:
contact_output += '\nand ' + children
contact_output += mail_to
print(contact_output)
print()
break
# Single only
else:
contact_output += person.full_name
contact_output += mail_to
print(contact_output)
print()