项目作者: Alexmhack

项目描述 :
command line intergration with python
高级语言: Python
项目地址: git://github.com/Alexmhack/cli_with_python.git
创建时间: 2018-09-03T05:24:23Z
项目社区:https://github.com/Alexmhack/cli_with_python

开源协议:

下载


cli_with_python

command line intergration with python

In this tutorial we will be continuing where we left off in previous tutorial series

If you haven’t started from there be sure to complete that tutorial first

So far we have made our command line app work with simple commands like

USAGE

  1. .../python_intermediate> python integrate --help
  2. .../python_intermediate> python integrate -h
  3. .../python_intermediate> python integrate view -id 1
  4. .../python_intermediate> python integrate view --user_id 10
  5. .../python_intermediate> python integrate message -id 1

In this tutorial we will be advancing our command line app.

Installation

Just clone the repository and you can all the above commands, add more data in data.csv
file and create your own commands by adding or editing code in __main__.py file.

Advancing Project

Create a new folder inside integrate folder and name it utils , copy paste the
__init__.py file in the utils fodler which makes utils a python module.

Now copy the templates.py file from python_intermediate/python_emailing and paste
it inside the utils folder.

Project Tree

  1. C:.
  2. └───integrate
  3. ├───python_csv
  4. ├───templates
  5. ├───utils
  6. └───__pycache__
  7. └───__pycache__

integrate folder

  1. C:.
  2. ├───python_csv
  3. ├───templates
  4. ├───utils
  5. └───__pycache__
  6. └───__pycache__

Now we will move some of the code from templates.py into __main__.py

templates.py

  1. import os
  2. def get_template_path(path):
  3. file_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), path)
  4. if not os.path.isfile(file_path):
  5. raise Exception(f"{file_path} is not a valid template path...")
  6. return file_path
  7. def get_template(path):
  8. file_path = get_template_path(path)
  9. return open(file_path).read()
  10. def render_context(template_string, context):
  11. return template_string.format(**context)
  1. __main__.py
  2. from utils.templates import get_template, render_context
  3. ...
  4. if args.type == "view":
  5. if args.user_id:
  6. print(find_user(user_id=args.user_id))
  7. if args.email:
  8. print(find_user(user_email=args.email))
  9. else:
  10. print("sending message")
  11. template = get_template(r'templates\email_message.txt')
  12. template_html = get_template(r'templates\email_message.html')
  13. context = {
  14. 'name': 'Pranav',
  15. 'date': '15th Aug, 18',
  16. 'total': 599
  17. }
  18. print(render_context(template, context))
  19. print(render_context(template_html, context))
  20. print("sending message...")

This will only give us one more functionality of priting the message that exists inside
the templates templates\email_message.txt and templates\email_message.html

Oh yes, where does these two templates come from, we have on more copy paste work left.
Copy the whole templates folder from python_intermediate and paste it in
integrate

We have made some changes in templates.py file.

  1. def get_template_path(path):
  2. file_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), path)
  3. ...

The file path argument that function takes looks into the integrate folder, you can print
the file_path to check. We achieve this using two os.path.dirname, first one
takes us back one folder and second tells to take from this current folder and then we join
the paths.

  1. .../cli_with_python> python integrate message
  2. # RESULT
  3. sending message
  4. Hi Pranav!
  5. Thank you for your purchase on 15th Aug, 18.
  6. We hope you are excited about using it.
  7. Just as a reminder the purchase total was $599.
  8. Have a greate one!
  9. Team Alexmhack
  10. <!DOCTYPE html>
  11. <html>
  12. <head>
  13. <title>Alexmhack Email Message</title>
  14. </head>
  15. <body>
  16. <h1>Hi Pranav!</h1>
  17. <p>
  18. Thank you for your purchase on 15th Aug, 18.
  19. We hope you are excited about using it.
  20. Just as a reminder the purchase total was $599.
  21. Have a greate one!
  22. </p>
  23. <p>Team Django</p>
  24. </body>
  25. </html>
  26. sending message...

As expected the contents of the file inside the templates gets printed.

Now we will be reducing some workload from __main__.py file and moving the code
to the data_class.py and creating a class there with our functions as methods of the
class.

Check the data_class.py file to understand what I mean.

Sending emails

Now that we have our template ready but it still lacks few things like rendering context
from the actual data.csv file as well as the date column is missing in our file
so for that I have written code using pieces of code from files in python_csv folder
in inserting_data.py which simply inserts the data.

Inside data_class.py file we have made some changes with message_user function
in the UserManager class.

  1. class UserManager:
  2. def message_user(self, user_id=None, user_email=None):
  3. user = self.get_user_data(user_id=user_id, user_email=user_email)
  4. if isinstance(user, dict):
  5. template = get_template(r'templates\email_message.txt')
  6. template_html = get_template(r'templates\email_message.html')
  7. context = user
  8. print(render_context(template, context))
  9. print(render_context(template_html, context))
  10. return None

Instead of passing the dict with key value pairs representing data, we just pass in the
user variable which is actually the returned value received from calling get_user_data
method with the user_id and user_email.

Since our function get_user_data returns dict objects as well as strings for error we
don’t wanna be inserting that in our context so we used a simple logic of checking if
the user is a dict object then executing further code

  1. if isinstance(user, dict):
  2. ...

That’s it now we can run commands

cmd

  1. .../cli_with_python> python integrate message -id 1
  2. sending message
  3. Hi Hopper!
  4. Thank you for your purchase on 2018-09-03 18:56:01.
  5. We hope you are excited about using it.
  6. Just as a reminder the purchase total was $269.
  7. Have a greate one!
  8. Team Django
  9. <!DOCTYPE html>
  10. <html>
  11. <head>
  12. <title>Django Email Message</title>
  13. </head>
  14. <body>
  15. <h1>Hi Hopper!</h1>
  16. <p>
  17. Thank you for your purchase on 2018-09-03 18:56:01.
  18. We hope you are excited about using it.
  19. Just as a reminder the purchase total was $269.
  20. Have a greate one!
  21. </p>
  22. <p>Team Django</p>
  23. </body>
  24. </html>

As you can see we have all the information of our user with id 1 rendered in the
template and the email is ready to be sent to the user at the email address.

NOTE: The user data we have used is fake with fake email addresses so you can use
your data and actually see the email being sent using actual email addresses.

For sending email we will be using the same code we used in the html_format_email.py
file. So copy paste the imports and email credentials from that file into

  1. Create a new method named ```render_message(self, user_data)``` in which we are going to
  2. do all the rendering stuff and in the message_user method we will send email
  3. **data_class.py**

class UserManager:

  1. def render_message(self, user_data):
  2. template = get_template(r'templates\email_message.txt')
  3. template_html = get_template(r'templates\email_message.html')
  4. if isinstance(user_data, dict):
  5. context = user_data
  6. plain = render_context(template, context)
  7. html = render_context(template_html, context)
  8. return (plain, html)
  9. return (None, None)
  1. So basically we moved all our rendering code into this method.
  2. Now for sending email we have
  3. **data_class.py**


def messageuser(self, user_id=None, user_email=None, all_users=False):
user = self.get_user_data(user_id=user_id, user_email=user_email)
if isinstance(user, dict):
plain
, html_ = self.render_message(user)
email = user.get(“email”, username)
to_list.append(email)

  1. try:
  2. email_conn = SMTP(host, port)
  3. email_conn.ehlo()
  4. email_conn.starttls()
  5. message = MIMEMultipart("alternative")
  6. message['Subject'] = "Hello there"
  7. message['From'] = 'Python Developer'
  8. message['To'] = email
  9. part_1 = MIMEText(plain_, 'plain')
  10. part_2 = MIMEText(html_, 'html')
  11. message.attach(part_1)
  12. message.attach(part_2)
  13. email_conn.login(username, password)
  14. email_conn.sendmail(from_email, to_list, message.as_string())
  15. return f"EMAIL SENT TO {email}"
  16. except SMTPException as e:
  17. print(e)
  18. finally:
  19. email_conn.quit()
  20. return None
  1. So we get the user details by caling ```get_user_data(user_id, user_email)``` on the
  2. user_id we received from ```__main__.py``` file where we call this class method passing
  3. in the user_id from the arguments provided in command prompt
  4. Then we do all the same stuff for sending email then at last we return the string
  5. containing email else we ```return None

Very simple and nicely called methods for completing the task.

Sending emails to all users

In __main__.py file we create another argument named --all_users which
does not take any argument and we do that using action="store_true" attribute

main.py

  1. parser.add_argument(
  2. "-all",
  3. "--all_users",
  4. action="store_true",
  5. help="sends message to all the users"
  6. )

Now we check if args contains -all or --all_users argument by

  1. elif args.type == "message":
  2. if args.all_users:
  3. print("SENDING EMAILS...")
  4. print(UserManager().message_all())
  5. else:
  6. print("SENDING EMAILS...")
  7. print(UserManager().message_user(user_id=args.user_id, user_email=args.email))

If yes then we call message_all() class method which simply opens the csvfile and
iterates over all the rows and sends emails by calling the

  1. **data_class.py**


def message_all(self):
with open(FILE_PATH) as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
row_id = int(row.get(“id”))
print(self.message_user(user_id=row_id))
```

We all print the returned message from function call

One more thing that is missing from our email sending command line app is that we don’t
have a tally which tells that which users are already been mailed, we have a field defined
for this purpose remember the sent field which is currently set to False.

What we will be doing now is editing that field to True if email has been sent and checking
the next time before sending emails that we don’t send email to users with True set as
sent field.