Questo articolo mostra come creare un'app App Engine in Python che invia email con annotazioni agli utenti chiedendo di confermare un abbonamento alla mailing list direttamente dalla Posta in arrivo e raccoglie gli abbonamenti in Datastore.
Prerequisiti e configurazione del progetto
Questa guida presuppone che tu abbia già installato l'SDK App Engine e che tu sappia come creare, eseguire e pubblicare progetti App Engine.
Innanzitutto, crea una directory per il tuo progetto. Inserisci tutti i file della tua applicazione in questa directory.
Copia il seguente codice in un file denominato app.yaml e sostituisci il segnaposto {{ APPID }} con l'ID univoco dell'app 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 file denominato main.py nella cartella del progetto App Engine e copia il seguente codice per configurare i gestori per la raccolta e l'elenco degli abbonamenti e per l'invio di email con annotazioni:
import webapp2
from emailsender import EmailSender
from subscribe import SubscribeHandler
app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)
Aggiungere dati strutturati all'email
Iniziamo con un'email molto semplice che chiede all'utente di confermare un abbonamento alla mailing list:
<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>
Puoi aggiungere dati strutturati in uno dei formati supportati (JSON-LD o Microdati) all'elemento head dell'email per definire il ristorante e aggiungere un'azione OneClickAction. Gmail supporta OneClickAction e mostra un'interfaccia utente specifica agli utenti per consentire loro di confermare l'abbonamento dalla Posta in arrivo.
Copia il seguente markup in un file denominato 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>
Microdati
<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="Approve Expense"/>
<div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
<link itemprop="url" href="https://myexpenses.com/approve?expenseId=abc123"/>
<meta itemprop="url" content="{{ confirm_url }}"/>
<link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
</div>
</div>
<meta itemprop="description" content="Approval request for John's $10.13 expense for office supplies"/>
</div>
<p>
Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
</p>
</body>
</html>
I dati strutturati sopra descrivono una mailing list denominata "XYZ" e un'azione ConfirmAction. Il gestore dell'azione è un HttpActionHandler che invia richieste POST all'URL specificato nella proprietà url.
Invio di richieste di abbonamento agli utenti
Copia il seguente codice in un file denominato emailsender.py nella cartella del progetto 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 classe EmailSender richiede che l'utente abbia eseguito l'accesso in modo che il suo indirizzo email possa essere recuperato. Quindi, carica il corpo dell'email da mail_template.html, sostituisce il segnaposto confirm_url con l'URL principale dell'app App Engine (https://APP-ID.appspot.com) e invia l'email all'utente attualmente connesso come se fosse lui stesso.
Raccolta ed elenco degli abbonamenti
Copia il seguente codice in un file denominato subscribe.py nella cartella del progetto 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 classe SubscribeHandlerclass listens to bothPOSTandGETrequests sent to the app root url (https://APP-ID.appspot.com).POSTrequests are used by Gmail to insert new subscriptions including theuser_id` parametro che corrisponde all'utente, come nel seguente esempio:
https://subscribe.appspot.com/?user_id=123abcd
Il gestore di richieste controlla semplicemente che l'user_id richiesto sia definito e poi memorizza l'abbonamento in Datastore. In questo modo, viene inviato a Gmail un codice di risposta HTTP 200 per segnalare la riuscita della richiesta. Se la richiesta non include il campo obbligatorio, il gestore di richieste restituirà un codice di risposta HTTP 400, segnalando la richiesta non valida.
Le richieste GET all'URL principale dell'app vengono utilizzate per elencare gli abbonamenti raccolti. Il gestore di richieste recupera prima tutti gli abbonamenti da Datastore e poi li stampa nella pagina, insieme a un semplice contatore.
Testare l'app
Esegui il deployment dell'app in App Engine e visita https://APP-ID.appspot.com/email (sostituisci APP-ID con l'ID dell'app App Engine) per inviare l'email con annotazioni a te stesso.

Dopo aver eseguito il deployment dell'app e inserito alcuni abbonamenti, visita l'app all'indirizzo https://APP-ID.appspot.com per visualizzare una pagina che riepiloga gli abbonamenti