Our Start-Up is 25% Pregnant π
Anvil is expanding. I mean, yes, I’ve gained a lot of new colleagues lately, but it’s not just that. Bridget and I are developing some pretty impressive baby bumps, and our newest teammate Stu is expecting a baby too – and all three babies are due in the new year!
Naturally, this has meant a lot of nerdy tracking of how the pregnancies are going. For some reason, baby sites seem obsessed with the idea of comparing the size of your baby to fruit and veg. So I decided to make my own fruit-based pregnancy app - with a twist.
We love Emoji π
Emoji are taking over the world! They’re everywhere you look, adding a dash of colour to texts, tweets and chats with friends and colleagues.
Emoji were initially invented for Japanese email and phone programs. They proved so popular the Unicode Consortium officially adopted them back in 2007 and has reserved a set of code points to represent emoji. Since then emoji have increased in number and spread around the world, replacing older ways of conveying emotion in text like :-)
. They’ve become a fun way to express ourselves that can cross language barriers.
Even Oxford Dictionaries joined in. They explained:
Oxford Dictionaries
Word of the Year 2015
So why not represent pregnancy with emoji too?
Python & Emoji π
I built my web app with Anvil, which means I could write everything in Python! This made emoji-wrangling particularly easy - Python 3 is great with Unicode, and most web browsers now support emoji display too. I previewed how they would look for different browsers using the Unicode site.
And with Anvil, that’s all we need to worry about! This is great news for those of us who like to express ourselves with the power of π.
Building the app π
Anvil’s online interface made it easy to start building my pregnancy tracker. I wanted a splash of colour so I chose the ‘Hello San Francisco’ theme and replaced the header image with a cute photo of a fruit-stall I found on pexels.com.
Font fun β
The best part was finding appropriate emoji for each week of pregnancy. There are quite a few fruits and veggies available as emoji but some creative choices had to be made!
I found it harder to pick emoji for the final weeks and really stretched the definition of “fruit or vegetable”! For example, for 37 weeks I resorted to π₯, and any time after the due date (40+ weeks) I just display ‘πΆ’. I’d started to run out of large vegetables π and most babies will make an appearance before this point.
Data tables π
I decided to use Anvil’s built in database to store the emoji I chose for each week. This was easier than trying to keep track of it all in my code.
You can store emoji directly in an Anvil Data Table - all Unicode is fully supported. But for convenience I stored emoji aliases in my Data Table instead, and made a little dict
to translate between the aliases and emoji to display. This made it easier to play around with which emoji corresponded to which week. And it made it easier to see what I was doing regardless of what browser I was using, as some don’t render all emoji.
EMOJI_LOOKUP = {'sparkles': 'β¨', 'salt': '\U0001f9c2', 'cherries': 'π', 'strawberry': 'π', 'mushroom': 'π', 'chestnut': 'π°', 'peanuts': 'π₯', 'kiwi': ':kiwi:', 'tangerine': 'π', 'peach': 'π', 'lemon': 'π', 'apple': ':apple:', 'avocado': 'π₯', 'onion': '\U0001f9c5', 'potato': 'π₯', 'mango': '\U0001f96d', 'banana': 'π', 'carrot': 'π₯', 'hot_pepper': 'πΆ', 'pear': 'π', 'ear_of_corn': 'π½', 'broccoli': '\U0001f966', 'leafy_green': '\U0001f96c', 'burrito': 'π―', 'eggplant': 'π', 'grapes': 'π', 'cucumber': 'π₯', 'pineapple': 'π', 'bread': 'π', 'sunflower': 'π»', 'bouquet': 'π', 'coconut': '\U0001f965', 'melon': 'π', 'green_salad': 'π₯', 'jack-o-lantern': 'π', 'watermelon': 'π', 'baby': 'πΆ'}
I generated EMOJI_LOOKUP
using Python’s emoji.emojize
module. Just pip install emoji
locally to give it a try yourself. Here’s what I did:
import emoji
# exported from table in Anvil with Server function
alias_list = ['sparkles', 'sparkles', 'sparkles', 'sparkles', 'salt', 'cherries', 'strawberry', 'mushroom', 'chestnut', 'peanuts', 'kiwi', 'tangerine', 'peach', 'lemon', 'apple', 'avocado', 'onion', 'potato', 'mango', 'banana', 'carrot', 'hot_pepper', 'pear', 'ear_of_corn', 'broccoli', 'leafy_green', 'burrito', 'eggplant', 'grapes', 'cucumber', 'pineapple', 'bread', 'sunflower', 'bouquet', 'coconut', 'melon', 'green_salad', 'jack-o-lantern', 'watermelon', 'baby', 'baby']
if __name__ == "__main__":
EMOJI_LOOKUP = {alias: emoji.emojize(":{}:".format(alias)) for alias in alias_list}
print(EMOJI_LOOKUP)
I only needed to run it once. Then I popped EMOJI_LOOKUP
into my app code. Notice that it doesn’t matter that some code points are represented long-form (like '\U0001f9c2'
) because I didn’t have the full emoji supporting fonts available in my local terminal. The Python interpreter in Anvil knows how to deal with that format, too.
Returning users π€©
Using the User Service I created a login flow for return users, so they can keep tracking their pregnancy throughout its duration without needing to redo their due date calculation each time they visit.
Emails π§
Users of the app can optionally sign up for weekly update emails. I did this by adding the Email Service and customised it with an unsubscribe option.
Each day, my app runs a Scheduled Task to send an email to those users who signed up for emails and have reached their next gestation week that day. Users get dropped from the table and email list after 43 weeks (at which point the baby should hopefully have made an appearance!)
Unsubscribing π
For obvious reasons, I wanted to make it as easy as possible for users to unsubscribe from the weekly emails. They shouldn’t need to log in to the app to do it.
But I had to make sure that people could only unsubscribe themselves, not other users. If I just set up a generic “Unsubscribe” API, it would be too easy for someone to accidentally (or maliciously) remove other people from the mailing list.
The solution: generate a secret token for each user. If we get a request with the right token, it must be from that user, and we can unsubscribe them immediately. I used token_urlsafe()
from the secrets module to generate a unique token for each user that is safe to use as part of a URL.
When the app emails a user, the message contains an unsubscribe url customised with their secret token:
"To unsubscribe, click here: {app}/_/api/unsubscribe/{unsub}".format(app=anvil.server.get_app_origin(), unsub=user['unsubscribe_token'])
I set up a custom HTTP API endpoint to handle clicks on those unsubscription links:
@anvil.server.http_endpoint("/unsubscribe/:id")
def unsubscribe_user(id):
"""
Enable users to unsubscribe from email updates by clicking this API link.
Check the id matches the unique unsubscribe token stored in the table against them
and if it does, remove that user's email subscription.
"""
ip = anvil.server.request.remote_address
user_to_unsubscribe = app_tables.users.search(unsubscribe_token=id)
for user in user_to_unsubscribe:
unsub_email = user['email']
user.update(signed_up_for_emails=False)
# Redirect user to app after unsubscribing them.
redirect = anvil.server.HttpResponse(302, f"Unsubscribed user {unsub_email} from IP {ip}, now trying to redirect")
redirect.headers['Location'] = "{}#unsubscribed".format(anvil.server.get_app_origin())
return redirect
return "Could not unsubscribe you - please check link is correct."
The corresponding code in the __init__
of the Form uses get_url_hash()
:
# If user got here by clicking unsubscribe link in their email
if get_url_hash() == "unsubscribed":
alert("You have been unsubscribed from Fruitmoji emails.")
Users just click the link that appears in their email, leading them to a popup in the app which confirms they’ve been removed from the mailing list.
Learn more π
If youβre interested in discovering more about how the βFruitmojiβ app works, take a look at the clone link below. It might be a helpful template for collecting a mailing list of your users with an unsubscribe mechanism:
Build your own app with Anvil
If you’re new here, welcome! Anvil is a platform for building full-stack web apps with nothing but Python. No need to wrestle with JS, HTML, CSS, Python, SQL and all their frameworks β just build it all in Python.
Want to build an app of your own? Get started with one of our tutorials:
Data Dashboard
Build Database-Backed Apps
Build a Simple Feedback Form
Build a data-entry app, and learn the techniques fundamental to building any Anvil app. In this tutorial, you will:
- Write server-side Python
- Build your User Interface
- Write client-side Python
- Store data in a database
- Deploy your app