Невозможно остановить цикл в исходном коде проекта - PullRequest
1 голос
/ 26 октября 2019

Я создаю собственного отправителя электронной почты в Python, используя в качестве входных данных образцы данных CSV. Я закончил большую часть сценария, однако единственная проблема, с которой я столкнулся, заключается в последних нескольких строках кода. Если вы запустите код (обязательно укажите свой адрес электронной почты / pwd), вы заметите, что пользовательские электронные письма отправляются нужным получателям, однако они получают несколько копий из-за цикла.

В конце я попробовал простое выражение break, которое просто отсылает первое письмо, а затем останавливается. Также не удалось найти решение на google / youtube.

import datetime
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import pandas as pd

host = "smtp.gmail.com"
port = 587
usr = os.environ.get("EMAIL_USER")
pwd = os.environ.get("EMAIL_PASS")
from_email = usr


class MessageUser():
    user_details = []
    messages = []
    email_messages = []
    base_message = "Hi {name}!\n\nThank you for the purchase on {date}. We hope you are exited about using it. Just as a reminder the purchase total was ${total}. Have a great day!"
    def add_user(self, name, amount, email=None):
        name = name[0].upper() + name[1:].lower()
        amount = f"{amount}"
        detail = {
            "name": name,
            "amount": amount,
        }
        today = datetime.date.today()
        date_text = '{today.month}/{today.day}/{today.year}'.format(today=today)
        detail['date'] = date_text
        if email is not None:   # if email != None
            detail["email"] = email
        self.user_details.append(detail)
    def get_details(self):
        return self.user_details
    def make_messages(self):
        if len(self.user_details) > 0:
            for detail in self.get_details():
                name = detail["name"]
                amount = detail["amount"]
                date = detail["date"]
                message = self.base_message
                new_msg = message.format(
                    name=name,
                    date=date,
                    total=amount
                )
                user_email = detail.get("email")
                if user_email:
                    user_data = {
                        "email": user_email,
                        "message": new_msg
                    }
                    self.email_messages.append(user_data)
                else:
                    self.messages.append(new_msg)
            return self.messages
        return []
    def send_email(self):
        self.make_messages()
        if len(self.email_messages) > 0:
            for detail in self.email_messages:
                user_email = detail['email']
                user_message = detail['message']
                try:
                    email_conn = smtplib.SMTP(host, port)
                    email_conn.ehlo()
                    email_conn.starttls()
                    email_conn.login(usr, pwd)
                    the_msg = MIMEMultipart("alternative")
                    the_msg["Subject"] = "Billing Update"
                    the_msg["From"] = from_email
                    the_msg["To"] = user_email
                    part_1 = MIMEText(user_message, "plain")
                    the_msg.attach(part_1)
                    email_conn.sendmail(from_email, [user_email], the_msg.as_string())
                except smtplib.SMTPException:
                    print("Error sending message.")
            return True
        return False

df = pd.read_csv('test_data.csv')

Name = df.Name
Amount = df.Price
Email = df.Email

for name, amount, email in zip(Name, Amount, Email):
    obj = MessageUser()
    obj.add_user(f'{name}', f'{amount}', email=f'{email}')
    obj.get_details()
    obj.send_email()

# Works but need loop break suggestion.
# Previous attempt with error. email.errors.HeaderParseError: header value appears to contain an embedded header.

name = df.Name[1:]
amount = df.Price[1:]
email = df.Email[1:]

obj = MessageUser()
obj.add_user(f'{name}', f'{amount}', email=f'{email}')
obj.get_details()
obj.send_email()

Ожидается: отправка пользовательских писем всем получателям один раз. Фактически: пользовательские электронные письма принимаются несколько раз из-за окончания цикла.

1 Ответ

0 голосов
/ 27 октября 2019

Скорее всего, проблема в том, что в вашем файле .csv есть повторные пользователи / электронные письма. Возможно, вам следует немного лучше структурировать свой код, чтобы легче было его отлаживать.

По сути, вы хотите убедиться, что вы разъединяете функциональные возможности, чтобы вы могли тестировать каждый из них по отдельности, так что сценарии, такие какэтого не происходит. В качестве примера возьмем следующее:

import pandas as pd

class User:
    def __init__(self, email, name, price):
        self.email = email
        self.name = name
        self.price = price

    def send_email_to_user(self):
        """ Sends email to the user. """
        SendEmail.send_email(self)

    def __eq__(self, other):
        """ Checks the equality of User by the standard == notation. """
        if (
            self.email == other.email
            and self.name == self.name
            and self.price == self.price
        ):
            return True
        else:
            return False


class SendEmail:
    @staticmethod
    def create_message(user):
        """ Creates the message template to be sent. """

        return f"<b>{user.name}: {user.email} for {user.price}</b>"

    @staticmethod
    def send_email(user):
        """ Sends email to passed user. """

        name = user.name
        email = user.email
        price = user.price

        message = SendEmail.create_message(user)

        print(f"Sending the message {message} to the user.")


data = {
    "name": ["name1", "name2", "name1", "name3"],
    "email": ["email1", "email2", "email1", "email3"],
    "price": ["$1", "$2", "$1", "$3"],
}
df = pd.DataFrame(data)

users = []
for name, email, price in zip(df.name, df.email, df.price):
    user = User(name, email, price)

    # We are using the __eq__ method here when we do "user not in users".
    if user not in users:
        users.append(user)
    else:
        print(
            f"It seems the user with the name {name} is already in the list of users."
        )

for user in users:
    user.send_email_to_user()

Выше выводов:

It seems the user with the name name1 is already in the list of users.
Sending the message <b>email1: name1 for $1</b> to the user.
Sending the message <b>email2: name2 for $2</b> to the user.
Sending the message <b>email3: name3 for $3</b> to the user.

Обратите внимание, как приведенный выше код разъединяет наши функциональные возможности. Таким образом, мы можем легче проверить, можем ли мы отправлять и шаблонировать электронные письма (через объект SendEmail), и, конечно, в конечном итоге, отправлять электронную почту пользователям через класс Users.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...