Ejemplo de extremo a extremo

En este documento, se muestra cómo compilar una app de App Engine en Python que envía correos electrónicos anotados a los usuarios para pedirles que confirmen una suscripción a una lista de distribución directamente desde su bandeja de entrada y recopila las suscripciones en Datastore.

Requisitos previos y configuración del proyecto

En esta guía, se supone que ya instalaste el SDK de App Engine y sabes cómo crear, ejecutar y publicar proyectos de App Engine.

Primero, crea un directorio para tu proyecto. Coloca todos los archivos de tu aplicación en este directorio.

Copia el siguiente código en un archivo llamado app.yaml y reemplaza el {{ APPID }} marcador de posición por tu ID único de la app de App Engine:

application: {{ APPID }}
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /.*
  script: main.app

libraries:
- name: jinja2
  version: latest

Crea un archivo llamado main.py en la carpeta de tu proyecto de App Engine y copia el siguiente código para configurar los controladores para recopilar y enumerar suscripciones, y para enviar correos electrónicos anotados:

import webapp2

from emailsender import EmailSender
from subscribe import SubscribeHandler

app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)

Agrega datos estructurados al correo electrónico

Comienza con un correo electrónico en el que se le pida al usuario que confirme una suscripción a una lista de distribución:

<html>
  <head>
    <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <p>
      Dear John, please confirm that you wish to be subscribed to the
      mailing list XYZ
    </p>
  </body>
</html>

Puedes agregar datos estructurados en uno de los formatos admitidos (JSON-LD o microdatos) al head del correo electrónico para definir el mensaje y agregar una OneClickAction. Gmail admite OneClickAction y muestra elementos de la IU que permiten a los usuarios confirmar su suscripción desde su bandeja de entrada.

Copia el siguiente lenguaje de marcado en un archivo llamado mail_template.html:

JSON-LD

<html>
  <head>
  <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <script type="application/ld+json">
    {
      "@context": "http://schema.org",
      "@type": "EmailMessage",
      "potentialAction": {
        "@type": "ConfirmAction",
        "name": "Confirm Subscription",
        "handler": {
          "@type": "HttpActionHandler",
          "url": "{{ confirm_url }}",
          "method": "http://schema.org/HttpRequestMethod/POST"
        }
      },
      "description": "Confirm subscription to mailing list XYZ"
    }
    </script>
    <p>
      Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
    </p>
  </body>
</html>

Microdatos

<html>
  <head>
    <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <div itemscope itemtype="http://schema.org/EmailMessage">
      <div itemprop="potentialAction" itemscope itemtype="http://schema.org/ConfirmAction">
        <meta itemprop="name" content="Confirm Subscription"/>
        <div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
          <meta itemprop="url" content="{{ confirm_url }}"/>
          <link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
        </div>
      </div>
      <meta itemprop="description" content="Confirm subscription to mailing list XYZ"/>
    </div>
    <p>
      Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
    </p>
  </body>
</html>

Los datos estructurados anteriores describen una lista de distribución llamada "XYZ" y una ConfirmAction. El controlador de la acción es un HttpActionHandler que envía solicitudes POST a la URL especificada en la propiedad url.

Importante: En el código anterior, reemplaza el {{ confirm_url }} marcador de posición por la URL de tu app de App Engine (como https://mailing-list.appspot.com) antes de enviar cualquier correo electrónico. El código que se describe en los siguientes pasos controla este reemplazo.

Envía solicitudes de suscripción a los usuarios

Copia el siguiente código en un archivo llamado emailsender.py en la carpeta del proyecto de App Engine:

import jinja2
import os
import webapp2

from google.appengine.api import mail
from google.appengine.api import users

from urlparse import urlparse

class EmailSender(webapp2.RequestHandler):

  def get(self):
    # require users to be logged in to send emails
    user = users.get_current_user()
    if not user:
      self.redirect(users.create_login_url(self.request.uri))
      return

    email = user.email()

    # The confirm url corresponds to the App Engine app url
    pr = urlparse(self.request.url)
    confirm_url = '%s://%s?user=%s' % (pr.scheme, pr.netloc, user.user_id())

    # load the email template and replace the placeholder with the confirm url
    jinja_environment = jinja2.Environment(
        loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
    template = jinja_environment.get_template('mail_template.html')
    email_body = template.render({'confirm_url': confirm_url})

    message = mail.EmailMessage(
        sender = email,
        to = email,
        subject = 'Please confirm your subscription to Mailing-List XYZ',
        html = email_body)

    try:
      message.send()
      self.response.write('OK')
    except:
      self.error(500)

La clase EmailSender requiere que el usuario acceda para que se pueda recuperar su dirección de correo electrónico. Luego, carga el cuerpo del correo electrónico desde mail_template.html, reemplaza el marcador de posición confirm_url por la URL raíz de la app de App Engine (https://APP-ID.appspot.com) y envía el correo electrónico al usuario que accedió.

Recopila y enumera suscripciones

Copia el siguiente código en un archivo llamado subscribe.py en la carpeta del proyecto de App Engine:

import webapp2

from emailsender import EmailSender
from google.appengine.ext import db


class SubscribeHandler(webapp2.RequestHandler):

  def post(self):
    user_id = self.request.get('user')

    # insert the subscription into the Datastore
    subscription = Subscription(user_id=user_id)
    subscription.put()

  def get(self):
    # retrieve up to 1000 subscriptions from the Datastore
    subscriptions = Subscription.all().fetch(1000)

    if not subscriptions:
      self.response.write('No subscriptions')
      return

    count = len(subscriptions)

    for s in subscriptions:
      self.response.write('%s subscribed<br/>' % (s.user_id))

    self.response.write('<br/>')
    self.response.write('%d subscriptions.' % (count))


class Subscription(db.Model):
    user_id = db.TextProperty(required=True)

La clase SubscribeHandler controla las solicitudes POST y GET enviadas a la URL raíz de la app (https://APP-ID.appspot.com). Gmail usa las solicitudes POST para insertar suscripciones nuevas, incluido el parámetro user_id que corresponde al usuario, como en el siguiente ejemplo:

https://subscribe.appspot.com/?user_id=123abcd

El controlador de solicitudes verifica que se defina el user_id requerido y almacena la suscripción en Datastore. Esto envía un código de respuesta HTTP 200 a Gmail para indicar que la solicitud se realizó correctamente. Si la solicitud no incluye el campo obligatorio, el controlador de solicitudes muestra un código de respuesta HTTP 400, lo que indica que la solicitud no es válida.

Las solicitudes GET a la URL raíz de la app muestran las suscripciones que se recopilaron. El controlador de solicitudes recupera todas las suscripciones de Datastore y las muestra en la página junto con un contador.

Prueba la app

Implementa tu app en App Engine y visita https://APP-ID.appspot.com/email (reemplaza APP-ID por el ID de tu app de App Engine) para enviarte el correo electrónico anotado.

Acciones en Gmail

Después de implementar tu app y de insertar suscripciones, visita https://APP-ID.appspot.com para ver una página que resuma las suscripciones.