Sending an email in celery while running the rest of the method - python

Im trying to make my request to a page faster by having the send email part done through celery so that way the rest of the method can run and finish without having to wait for the email to be sent.
Ive tried using async and making a celery call before but I am unskilled and need a little help in that department
def checkout():
if request.method == 'POST':
username = User.get_username(session['email'])
#todo: do the next line in celery
send_email(username, session['email_to'])
session['email_to'] = None
flash('Youre request has been sent')
return redirect(url_for('home'))
return render_template('transaction/checkout.html')
send_email.py
def send_email(username_from, email):
from_email = os.get('email')
from_password = 'rnmmhgtlcaqsxows'
to_email = os.get('email')
subject = f'New Request from {username_from}'
message = f"You have a request from {username_from}"
msg = MIMEText(message, 'html')
msg['subject'] = subject
msg['To'] = to_email
msg['From'] = from_email
gmail = smtplib.SMTP('smtp.gmail.com',587)
gmail.ehlo()
gmail.starttls()
gmail.login(from_email, from_password)
gmail.send_message(msg)

Imagine send_email is a Celery task. - It would be sufficient to have
send_email.apply_async()
in your code to put that task on the default Celery queue, and it will be picked and executed by Celery, while the rest of the code continues working.

Related

Flask Mail, Duplicate mails being sent

I am using Python and Flask Mail. I am trying to send a welcome email after the user confirms his account via clicking on a link sent as an email to the user earlier for confirming his account. After confirming his account by changing the value of the confirmed entry to true in the database, I send the welcome email. But it is sending 5 duplicate emails instead of one.
I used the below code-
class AccountConfirmation(Resource):
#jwt_required
def post(self):
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
user.confirmed = True
db.session.commit()
send_welcome_email(user)
return {'msg': 'Account confirmed successfully. You can now login to your account'}, 200
def send_welcome_email(user):
subject = '...'
body = '...'
sender = Config.ADMINS[0]
recipients = [user.email_id]
send_email(subject, body, sender, recipients)
from flask_mail import Message
def send_email(subject, body, sender, recipients):
msg = Message(subject = subject, body = body, sender=sender, recipients=recipients)
mail.send(msg)
I don't find any problem with the code. Except if there are multiple request to send the mail. To get over it, you can try sending Asynchronous Email. Please check this piece of code and don't forget to check the link for better explanation.
from threading import Thread
# ...
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
Thread(target=send_async_email, args=(app, msg)).start()

Routing Twilio SMS Responses in Python for conversational bot

I'm a university student relatively new to Python programming. I have been trying to build a conversational bot that allows a user to schedule appointments via SMS in Python.
I have been following many Python Twilio SMS tutorials online such as the official Twilio guide. I cannot figure out how to route a Twilio SMS response to the corresponding route. For example, a user is supposed to text in either SCHEDULE, RESCHEDULE, CHECK, or DROP, and the application is supposed to route them to the corresponding route with some logic to process the request (and reply with a further SMS message). If they do not enter one of the four specified words, the application is supposed to reroute the user back to the /inbound route to ask them to again clarify that they are selecting one of the four acceptable options. I have really been struggling hard for the past couple of days on this, getting confused on routes and whether or not to use the message = client.messages.create or response = MessagingResponse() Twilio APIs. I am using Flask to handle the web server capabilities.
Also, would you happen to know how to pass a parameter through the redirect route so that I can use that data in the next route and save it in a database?
Thank you so much. I greatly appreciate all the help I can get.
from twilio.twiml.messaging_response import Body, Message, Redirect, MessagingResponse
from flask import Flask, request, session
app = Flask(__name__)
# Main function triggered by a POST request by ngrok. When an SMS is received, Twilio will send the POST.
#app.route('/inbound', methods=['POST'])
def inbound():
""" Use Twilio API to reply to texts """
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip() # formulate answer to message
if "schedule" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you schedule your appointment today. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/schedule')
# write to db
elif "reschedule" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you reschedule your appointment today. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/reschedule')
elif "appointments" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you check your upcoming appointment. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/check')
elif "drop" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you cancel your appointment. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/drop')
else:
reply = 'Hi, my name is Cletus. How can I help you? You can text RESCHEDULE to reschedule you appointment, ' \
'DROP to cancel your appointment, and APPOINTMENTS to receive a list of your currently scheduled ' \
'appointments. Have a great day!'
response.message(reply)
response.redirect('/inbound')
return str(response)
#app.route('/schedule', methods=['POST'])
def schedule():
print('Finally routing to schedule')
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
#app.route('/reschedule', methods=['POST'])
def reschedule():
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
#app.route('/check', methods=['POST'])
def check():
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
#app.route('/drop', methods=['POST'])
def cancel():
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
if __name__ == '__main__':
app.run(debug = True)

django_cron not sending email

I have just installed django_cron not django_crontab though, somehow I am trying to send an email as notification but it just wouldn't work.
this is just for testing purpose so I set it to 1 minute.
the code was a bit more complex before but it didn't work so I used the most simplified way to send email in order to make sure that it's not working. Also even used it as a post method to test, and it tested out working perfectly if a post method called the follow codes
class MyCronJob(CronJobBase):
RUN_EVERY_MINS = 1 # every minute
schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
code = 'Email_Test' # a unique code
def do(self):
print('######################################')
send_mail(
'Subject here from cron',
'Here is the message.',
'from#email.com',
['to#emailcom'],
fail_silently=False,
)
I tried running python manage.py runcrons and python manage.py runcrons --force and waited, (no errors, I also added the print because I want to see if the code even runs and good, I see the ################# got printed)
can someone please give me an advise?
Thanks in advance
Try a custom function for sending mail. I tried send_mail function and it did not work for me and i did not find any bug or explanation on this topic. Below is an example of sending html emails using smtplib.
import smtplib
def send_email(email, subject, message):
text = subject
html = message
msg = MIMEMultipart('alternative')
html_part = MIMEText(html, 'html')
text_part = MIMEText(text, 'plain')
msg.attach(text_part)
msg.attach(html_part)
msg['Subject'] = subject
msg['From'] = SENDER_EMAIL
msg['To'] = email
s = smtplib.SMTP(settings.GMAIL_SMTP)
s.ehlo()
s.starttls()
s.login(SENDER_EMAIL, SENDER_PASSWORD)
s.sendmail(SENDER_EMAIL, email, msg.as_string())
s.quit()
please make sure first you must have to import library from django.core.mail, to using send_mail. I know it's too late to help, but u can try this..
from django_cron import CronJobBase, Schedule
from django.core.mail import send_mail
class my_scheduled_job(CronJobBase):
RUN_EVERY_MINS = 1
schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
code = 'user_dashboard.autoemail.my_scheduled_job'
def do(self):
subject= 'Send Email With Automatic Schedule'
message= 'Test send email :'
email_to= ['xxxxxxxx#gmail.com']
email_user(subject_test, message_test, email_to_test)
print ("done")
def email_user(subject, message, email_to):
email_from = 'noreply#xxxx.id'
send = send_mail(subject, message, email_from, email_to)
return send

Send an email via. deferred library in Google App Engine

I'm attempting to send mail using the EmailMessage class, which, when used normally, as in:
message = mail.EmailMessage()
message.sender = ...
message.to = ...
message.subject = ...
message.send()
Works just fine; I receive the email as expected.
However, I am trying to add this email.send() event to the push queue using the deferred library:
def email():
message = mail.EmailMessage()
message.sender = ...
message.to = ...
message.subject = ...
// elsewhere
def send_email(message):
deferred.defer(message.send, _countdown=10)
app = webapp2.WSGIApplication([
('/api/email', EmailHandler)
], debug=False)
I can see it is successfully added to the push queue on the admin interface, but I never actually receive the email or any kind of failure notification/bounce message.
I've seen the limitations of the deferred library but don't think I'm running into any of those here?
deferred.defer takes as arguments a function and arguments to be passed to that function. When you do this:
deferred.defer(message.send, _countdown=10)
You pass the function message.send but all the data from the message object gets lost so you no longer have any data!
Instead move all of the logic into your send_email function:
def send_email(from, to, subject, body):
message = mail.EmailMessage()
message.sender = ...
message.to = ...
message.subject = ...
message.send()
// elsewhere
deferred.defer(send_email, from, to, subject, body, _countdown=10)

How can I send HTML email via Celery? It keeps sending in text/plain

I setup a system Django/Celery/Redis. And I used EmailMultiAlternatives to send my HTML and Text email.
When I send email as part of the request process, the email is sent in HTML. All runs well and it wraps around a function. Here's the code:
def send_email(email, email_context={}, subject_template='', body_text_template='',
body_html_template='', from_email=settings.DEFAULT_FROM_EMAIL):
# render content
subject = render_to_string([subject_template], context).replace('\n', ' ')
body_text = render_to_string([body_text_template], context)
body_html = render_to_string([body_html_template], context)
# send email
email = EmailMultiAlternatives(subject, body_text, from_email, [email])
email.attach_alternative(body_html, 'text/html')
email.send()
However, when I tried to run it as a Celery Task, like below, it just sent as "text/plain". What could be the problem? Or what can I do to find out more? Any hint or solution are greatly appreciated.
#task(name='tasks.email_features', ignore_result=True)
def email_features(user):
email.send_email(user.email,
email_context={'user': user},
subject_template='emails/features_subject.txt',
body_text_template='emails/features_body.txt',
body_html_template='emails/features_body.html')
Celery does not affect the executing results of tasks. Have you restarted celeryd after make change to the task? It's important for celery to reload the Python code.
When you were using EmailMultiAlternatives and email.attach_alternative(body_html, 'text/html'), the email was sent in the Content-Type: multipart/alternative; and the text/html is an alternative one, it depends on mail receipts to choose the content type of the mail during the rendering. So is the receipt same one between the view procedure and celery procedure?
You could output the sending mail directly, through python -m smtpd -n -c DebuggingServer localhost:25 to find out the actual messages. I've tested on my mac w/ redis-backed Celery, the outputs of the examples taken from the official doc are same as expected.
from django.core import mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags
class SendEmail(Task):
name="send_email"
def run(self,email):
subject = 'Daily News Letter'
html_message = render_to_string('letter.html', {'context': 'values'})
plain_message = strip_tags(html_message)
from_email = env('EMAIL_HOST_USER')
mail.send_mail(subject, plain_message, from_email, [email], html_message=html_message)
return None
send_email = celery_app.register_task(SendEmail())

Categories