Day 20 of the Anvil Advent Calendar

Build a web app every day until Christmas, with nothing but Python!

Spread the Christmas spirit

It’s December 20th – probably time to get those Christmas cards sent out! Even better, why not send personalised e-cards?

https://merry-christmas.anvil.app

In today’s advent app, we show you how to create custom Christmas e-cards and send them out to all your friends and family.

Create your app

We’ll create a simple UI using TextBoxes and TextAreas for our user inputs.

To create our custom e-card, we’ll ask our users for:

  • the background image they’d like to use
  • the recipient’s email address
  • the recipient’s name
  • their personal greeting
  • their name

Our basic UI looks like this:

Choose your background

We’ll use Anvil’s Data Tables to store background images for our users to choose from when creating their card.

We create a table called ‘backgrounds’ which has a text column for the name of the image and a media column to store the images. I have uploaded three images to my Data Table which looks like this:

Preview backgrounds

Back in the UI, we’ll use a DropDown component to let our users choose the background image for their e-card. We’ll also show a preview of the selected image using an Image component.

We add this code to our Form’s __init__ method to:

  1. Populate the DropDown
  2. Show a preview of the image
    # get images from the data table
    cards = app_tables.backgrounds.search()
    # populate the DropDown
    self.card_dropdown.items = [(x['name'], x) for x in cards]
    # Show a preview of the image
    self.card_image.source = self.card_dropdown.selected_value['image']

We’ll also change the image preview when the user chooses a different image. We do this in our card_dropdown_change function which runs when the user selects a different value from the DropDown:

def card_dropdown_change(self, **event_args):
    card = self.card_dropdown.selected_value
    self.card_image.source = card['image']

Capture user inputs

All that’s left to do is capture user inputs and then send them to a Server Module which will handle sending the e-card.

We’ll send the inputs to the server when the ‘Send my e-card’ button is clicked:

def button_1_click(self, **event_args):
    email_address = self.email_box.text
    recipient = self.recipient_box.text
    sender = self.sender_box.text
    greeting = self.greeting_box.text
    image = self.card_image.source
    anvil.server.call('send_ecard', image, email_address, recipient, greeting, sender)

Send emails

We’ll need to enable the Anvil Email Service - just click the ‘+’ next to Services in the App Browser. We can now use anvil.email.send to send emails from our app.

Once that’s enabled, we add a Server Module and define our send_ecard() function to send our custom e-card. We use inline attachments to include the background image:

@anvil.server.callable
def send_ecard(image, email_address, recipient, greeting, sender):
  anvil.email.send(inline_attachments={'img':image},from_name="The Anvil Christmas elves", 
               to=email_address, 
               subject="Merry christmas from {}".format(sender),
               text="Please open in a different email service to see your e-card in its true glory",
               html=f"""
               <div style="background: url(cid:img); background-size: cover; width: 100%; ">
                 <div style="min-height:400px; padding: 30px;">
                  <p style="font-size: 54px; color: white; font-family: 'Snell Roundhand';">Merry Christmas {recipient}!</p>
                  <p style="font-size: 32px; color: white;">{greeting}</p>
                  <p style="font-size: 22px; color: white;">From {sender}</p>
                </div>
               </div>
               """
               )

Outgoing email is subject to a monthly quota, depending on your account plan.

Email sent through a custom SMTP server does not count towards your monthly quota.

Voila!

That’s it!

And that’s it! Go forth and spread the Christmas spirit!




Give the Gift of Python

Share this post: