AnswerBun.com

Python Script that sends emails to a list of people and their emails and attaches specific documents (w/ tkinter menu)

Code Review Asked by rmcknst2 on January 2, 2022

This is my first Python project. Any tips on making it more efficient, less cluttered, and could you think of some cases that I haven’t in which my code will break.

For clarification the vendor list csv file is set up like this:

[vendorname][email1][email2]...[email-n]

There are probably also some left-over test print() lines in there.

from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from configparser import ConfigParser
from os import startfile
import yagmail
import csv
import time
import os.path
from sys import exit
from keyring import *
from keyring import get_keyring
import keyring.backends.Windows
import smtplib

keyring.backends.Windows.WinVaultKeyring
print(get_keyring())
print("------------")
def open_vend_direct():
    vend_directory = filedialog.askopenfilename(
        initialdir="/", title="Select file", filetypes=(("Excel Files (CSV)", "*.csv"), ("all files", "*.*")))
    parser = ConfigParser()
    parser.read('config.ini')
    parser.set('VendorList', 'List_Location', vend_directory)
    with open('config.ini', 'w') as f:
        parser.write(f)
    vend_text.set(vend_directory)


def open_attach_direct():
    vend_attach_direct = filedialog.askdirectory()
    parser = ConfigParser()
    parser.read('config.ini')
    parser.set('VendorFile', 'file_Location', vend_attach_direct)
    with open('config.ini', 'w') as f:
        parser.write(f)
    attach_text.set(vend_attach_direct)


def open_log_direct():
    log_locate = filedialog.askdirectory()
    parser = ConfigParser()
    parser.read('config.ini')
    parser.set('LogFolder', 'log_location', log_locate)
    with open('config.ini', 'w') as f:
        parser.write(f)
    log_text.set(log_locate)


def apply_option():
    checkbox1 = open_log.get()
    if checkbox1 == 0:
        checkbox1 = '0'
    else:
        checkbox1 = '1'

    checkbox2 = send_log.get()
    if checkbox2 == 0:
        checkbox2 = '0'
    else:
        checkbox2 = '1'

    parser = ConfigParser()
    parser.read('config.ini')
    parser.set('LogFolder', 'auto_open', checkbox1)
    parser.set('LogFolder', 'send_default', checkbox2)
    with open('config.ini', 'w') as f:
        parser.write(f)


def change_operator_email():

    def apply_operator():

        def ok():
            error_window.destroy()

        str_operator = str(operator.get())

        if not str_operator:
            error_window = Toplevel()
            error_window.title("Error")
            error_label = ttk.Label(
                error_window, text="Invalid Entry").grid(row=1, column=2)
            ok_button = ttk.Button(
                error_window, text="Ok", command=ok).grid(row=2, column=2)
            empt_label1 = ttk.Label(error_window, text='').grid(
                column=1, row=1, sticky=W)
            empt_label2 = ttk.Label(error_window, text='').grid(
                column=3, row=1, sticky=W)
            for child in error_window.winfo_children():
                child.grid_configure(padx=5, pady=9)
        else:
            parser.set('AccountInfo', 'operator_email', str_operator)
            with open('config.ini', 'w') as f:
                parser.write(f)
            operator_text.set(str_operator)
            op_window.destroy()

    op_window = Toplevel()
    op_window.title("Operator Email Change")

    change_operator_email_prompt = ttk.Label(
        op_window, text='--New Operator Email--').grid(row=0, column=2)
    msg1 = ttk.Label(op_window, text='Email: ',).grid(row=1, column=1)
    operator = StringVar()
    operator_entry = ttk.Entry(
        op_window, textvariable=operator).grid(row=1, column=2)
    apply_operator_button = ttk.Button(
        op_window, text='Apply', command=apply_operator).grid(row=1, column=3)

    for child in op_window.winfo_children():
        child.grid_configure(padx=5, pady=9)

    return


def quit_option_menu():
    exit()


def change_credentials():

    def apply_cred():

        def ok():
            error_window.destroy()

        str_email = str(cred_email.get())
        str_pass = str(cred_pass.get())

        if not str_email:
            error_window = Toplevel()
            error_window.title("Error")
            error_label = ttk.Label(
                error_window, text="Invalid Entry").grid(row=1, column=2)
            ok_button = ttk.Button(
                error_window, text="Ok", command=ok).grid(row=2, column=2)
            empt_label1 = ttk.Label(error_window, text='').grid(
                column=1, row=1, sticky=W)
            empt_label2 = ttk.Label(error_window, text='').grid(
                column=3, row=1, sticky=W)
            for child in error_window.winfo_children():
                child.grid_configure(padx=5, pady=9)

        elif only_email.get() == 1:
            parser.set('AccountInfo', 'sender_email', str_email)
            with open('config.ini', 'w') as f:
                parser.write(f)

        elif not str_pass:
            error_window = Toplevel()
            error_window.title("Error")
            error_label = ttk.Label(
                error_window, text="Invalid Entry").grid(row=1, column=2)
            ok_button = ttk.Button(
                error_window, text="Ok", command=ok).grid(row=2, column=2)
            empt_label1 = ttk.Label(error_window, text='').grid(
                column=1, row=1, sticky=W)
            empt_label2 = ttk.Label(error_window, text='').grid(
                column=3, row=1, sticky=W)
            for child in error_window.winfo_children():
                child.grid_configure(padx=5, pady=9)
        else:
            parser.set('AccountInfo', 'sender_email', str_email)
            with open('config.ini', 'w') as f:
                parser.write(f)

            keyring.set_password('yagmail', str_email, str_pass)
        return

    def yag_tip():

        return

    def hide_pass():
        if only_email.get() == 1:
            hide_msg = ttk.Label(
                credential_window, text="                                                            ").grid(row=4, column=2)
        else:
            new_password = Entry(
                credential_window, textvariable=cred_pass).grid(row=4, column=2)

    credential_window = Toplevel()
    credential_window.title("Change Credentials")

    only_email = IntVar(0)
    only_email_check = ttk.Checkbutton(
        credential_window, text='Email Change Only -- This means you have already set the credentials up', variable=only_email, command=hide_pass).grid(column=2, row=1)

    change_credentials_prompt = ttk.Label(
        credential_window, text="Change Gmail Credentials for Yagmail").grid(row=2, column=2)
    msg2 = ttk.Label(credential_window, text="New Email: ").grid(
        row=3, column=1)
    msg2 = ttk.Label(credential_window, text="New Password: ").grid(
        row=4, column=1)

    cred_email = StringVar()
    cred_pass = StringVar()
    new_email = Entry(credential_window, textvariable=cred_email).grid(
        row=3, column=2)
    new_password = Entry(
        credential_window, textvariable=cred_pass).grid(row=4, column=2)

    apply_cred_button = ttk.Button(
        credential_window, text='Apply', command=apply_cred).grid(row=5, column=3)
    tip_button = ttk.Button(
        credential_window, text='Yagmail Credential Tips', command=yag_tip).grid(row=5, column=1)

    for child in credential_window.winfo_children():
        child.grid_configure(padx=5, pady=9)


def run_vendsend():

    parser = ConfigParser()
    parser.read('config.ini')
    version = 1.9
    # ---------------------------------------------------------------------------------------
    yag = yagmail.SMTP(parser.get('AccountInfo', 'sender_email'))

    def csv_reader(filename):
        vendor_names, vendor_emails = ([] for i in range(2))
        csv_file = filename
        with open(csv_file) as csvfile:
            readCSV = csv.reader(csvfile, delimiter=',')
            num_col = len(next(readCSV))

        # main csv reader
        with open(csv_file) as csvfile:
            readCSV = csv.reader(csvfile, delimiter=',')
            for row in readCSV:
                name = row[0]
                vendor_names.append(name)

                email = []
                for i in range(1, num_col):
                    email.append(row[i])
                vendor_emails.append(email)

        final_list = [[vendor_names[x]] + [vendor_emails[x]]
                      for x in range(len(vendor_names))]

        return final_list

    def no_email_check(vendors):
        filt_count = 0
        list1 = vendors
        for i in (list1):
            filter = [""]
            for u in filter:
                while True:
                    try:
                        list1[filt_count][1].remove(u)
                    except:
                        break
            filt_count += 1

        flagged = [contact[0] for contact in list1 if not contact[1]]

        return flagged

    def email_filter(vendors):
        filt_count = 0
        list1 = vendors
        for i in (list1):
            filter = [""]
            for u in filter:
                while True:
                    try:
                        list1[filt_count][1].remove(u)
                    except:
                        break
            filt_count += 1

        for k in range(len(list1) - 1, 0, -1):
            if not list1[k][1]:
                list1.pop(k)
        return list1

    def name_filter(vendors):
        unfiltered = vendors[:]
        filtered = []
        for i in unfiltered:
            filtered.append([i[0].strip(' '), i[1]])
        return filtered

    def reporter_tool(removed, invalid_e, n_attach):
        vendor_removed = removed
        invalid_emails = invalid_e
        no_attach = n_attach

        timestr = time.strftime("%Y-%m-%d-(%H-%M-%S)")
        parser = ConfigParser()
        parser.read('config.ini')
        reporter_name = (parser.get('LogFolder', 'Log_Location') +
                         '\vendsend_log_' + timestr + '.txt')
        fh = open(reporter_name, 'w')
        fh.write('-----Vendors With No Emails-----n')
        f_len = len(vendor_removed)
        fcount = 0
        if f_len > 0:
            while fcount < f_len:
                fh.write(vendor_removed[fcount])
                fh.write("n")
                fcount += 1
        else:
            fh.write("nulln")

        fh.write('n')

        fh.write('-----Invalid Emails-----n')
        f_len = len(invalid_emails)
        fcount = 0
        if f_len > 0:
            while fcount < f_len:
                for i in invalid_emails:
                    fh.write("%sn" % i)
                fh.write("n")
                fcount += 1
        else:
            fh.write("nulln")

        fh.write('n')

        fh.write("-----Vendors with no Attachments-----n")
        f_len = len(no_attach)
        fcount = 0
        if f_len > 0:
            while fcount < f_len:
                fh.write(no_attach[fcount])
                fh.write("n")
                fcount += 1
            fh.write("n***Possible causes:n")
            fh.write("   -Invalid Directory Path n")
            fh.write(
                "   -Vendor name from the name file doesn't match the file name n")
        else:
            fh.write("nulln")

        fh.close()
        if open_log.get() == 1:
            os.startfile(reporter_name)

        if send_log.get() == 1:
            yag.send(parser.get('AccountInfo', 'operator_email'),
                     ('Log for ' + timestr), reporter_name)

    vendor_csv = parser.get('VendorList', 'List_Location')
    attach_directory = (parser.get('VendorFile', 'File_Location') + "\")
    vendor_info = csv_reader(vendor_csv)
    vendor_removed = no_email_check(vendor_info)
    vendor_info = email_filter(vendor_info)
    vendor_info = name_filter(vendor_info)

    # -----test purposes only-----
    #v_len = len(vendor_info)
    #tcount = 0
    # while tcount < v_len:
    # print(vendor_info[tcount])
    # print()
    #tcount += 1

    # print('----------------Reporter---------------')
    #r_len = len(vendor_removed)
    #rcount = 0
    # while rcount < r_len:
    # print(vendor_removed[rcount])
    # print()
    #rcount += 1

    vendor_count = 0
    num_vendors = len(vendor_info)
    invalid_emails = []
    no_attach = []

    while vendor_count < num_vendors:
        recipient = vendor_info[vendor_count][1]
        subject = 'Open PO' + ' for ' + vendor_info[vendor_count][0]

        attachment = attach_directory + vendor_info[vendor_count][0] + '.txt'
        if os.path.isfile(attachment) == True:
            pass
        else:
            no_attach.append(vendor_info[vendor_count][0])
            vendor_count += 1
            continue
        contents = ['-Automated Email Test-', 'Hi ' + vendor_info[vendor_count][0] +
                    '. Attached below is the document of your open POs', 'DO NOT REPLY', attachment]
        try:
            yag.send(recipient, subject, contents)
        except yagmail.error.YagInvalidEmailAddress:
            invalid_emails.append(vendor_info[vendor_count])
            print('error.YagInvalidEmailAddress for ' +
                  vendor_info[vendor_count][0] + 'n')
        except yagmail.error.YagAddressError:
            invalid_emails.append(vendor_info[vendor_count])
            print('error.YagAddressError for ' +
                  vendor_info[vendor_count][0] + 'n')
        except smtplib.SMTPAuthenticationError:
            print('one')
            smtb_window = Toplevel()
            smtb_window.title("Error")
            error_header = ttk.Label(
                smtb_window, text="---AUTHENTICATION ERROR---").grid(column=1, row=1)
            error_message = ttk.Label(
                smtb_window, text="smtplib.SMTPAuthenticationError: (534, b'5.7.9 Application-specific password required.").grid(column=1, row=2)
            error_message2 = ttk.Label(
                smtb_window, text="Learn more at https://support.google.com/mail/?p=InvalidSecondFactor ").grid(column=1, row=3)
            empt_label = ttk.Label(optionmenu, text='').grid(
                column=1, row=4, sticky=W)
            error_message3 = ttk.Label(
                smtb_window, text="This error means you need to set up an ""app password" + " within gmail.").grid(column=1, row=5)

            for child in smtplib.winfo_children():
                child.grid_configure(padx=5, pady=9)
            return
        vendor_count = vendor_count + 1

    reporter_tool(vendor_removed, invalid_emails, no_attach)
    exit()


parser = ConfigParser()
parser.read('config.ini')

root = Tk()
root.title("VendSend Menu")

optionmenu = ttk.Frame(root, padding="3 3 12 12")
optionmenu. grid(column=0, row=0, sticky=(N, W, E, S))

root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

###############################
# row 1
vend_list_button = ttk.Button(optionmenu, text='Vendor List Directory',
                              command=open_vend_direct).grid(column=1, row=1, sticky=W)
vend_text = StringVar()
vend_text.set(parser.get('VendorList', 'list_location'))
vend_list_locat = ttk.Label(optionmenu, textvar=vend_text).grid(
    column=2, row=1, sticky=(W, E))

############################
# row 2
vend_attach_button = ttk.Button(optionmenu, text='Vendor File Directory',
                                command=open_attach_direct).grid(column=1, row=2, sticky=W)
attach_text = StringVar()
attach_text.set(parser.get('VendorFile', 'file_location'))
vend_attach_locat = ttk.Label(optionmenu, textvar=attach_text).grid(
    column=2, row=2, sticky=(W, E))

###########################
# row 3
log_location_button = ttk.Button(
    optionmenu, text='Log Folder Preference', command=open_log_direct).grid(column=1, row=3, sticky=W)
log_text = StringVar()
log_text.set(parser.get('LogFolder', 'log_location'))
log_locat = ttk.Label(optionmenu, textvar=log_text).grid(
    column=2, row=3, sticky=(W, E))

open_log = IntVar(value=parser.get('LogFolder', 'auto_open'))
open_log_check = ttk.Checkbutton(
    optionmenu, text='Open Log Automatically After Script End', variable=open_log, command=apply_option).grid(column=3, row=3, sticky=W)

##########################
# row 4
empt_label = ttk.Label(optionmenu, text='').grid(column=1, row=4, sticky=W)
###########################
# row 5
operator_email_button = ttk.Button(optionmenu, text='Change Operator Email',
                                   command=change_operator_email).grid(column=1, row=5, sticky=W)
operator_text = StringVar()
operator_text.set(parser.get('AccountInfo', 'operator_email'))
operator_label = ttk.Label(optionmenu, textvar=operator_text).grid(
    column=2, row=5, sticky=W)
send_log = IntVar(value=parser.get('LogFolder', 'send_default'))
send_log_check = ttk.Checkbutton(
    optionmenu, text='Send Log To Operator Email', variable=send_log, command=apply_option).grid(column=3, row=5, sticky=W)

##########################
# row 6
change_credentials_button = ttk.Button(
    optionmenu, text='Change Program Credentials', command=change_credentials).grid(column=1, row=6, sticky=W)
###########################
# row7
empt_label1 = ttk.Label(optionmenu, text='').grid(column=1, row=7, sticky=W)
###########################
# row 8
apply_changes = ttk.Button(optionmenu, text='Apply Changes',
                           command=apply_option).grid(column=1, row=8, sticky=W)
run_program = ttk.Button(optionmenu, text='Run Program',
                         command=run_vendsend).grid(column=2, row=8, sticky=W)
quit_option = ttk.Button(optionmenu, text='Quit', command=quit_option_menu).grid(
    column=3, row=8, sticky=W)

for child in optionmenu.winfo_children():
    child.grid_configure(padx=5, pady=9)
root.mainloop()

Add your own answers!

Related Questions

Google Maps marker management for games

1  Asked on October 27, 2021 by joeyboy

       

Check if array has the same number of even and odd values in Python

11  Asked on February 26, 2021 by uncalled-astronomer

   

Transpose of a matrix using Python 3.8

2  Asked on February 25, 2021

   

Angular Typescript Async queue service

0  Asked on February 22, 2021 by leonel-franchelli

         

A simple terminal-based trading game in C

2  Asked on February 20, 2021 by redwolf-programs

   

Python wrapper for official Hacker News API

1  Asked on February 15, 2021

         

Implementing a Directed and Undirected Graph in Java

1  Asked on February 14, 2021 by msmilkshake

   

c++ shell for linux

2  Asked on February 14, 2021 by the-masked-rebel

     

Excel blank row inserter

2  Asked on February 12, 2021 by sandro4912

       

Private VBA Class Initializer called from Factory #2

0  Asked on February 8, 2021 by cristian-buse

     

Perl – Splitting a string

3  Asked on February 7, 2021 by linny

     

Hackerrank’s Queen’s Attack II

2  Asked on February 6, 2021 by bork

     

C++17 thread pool

2  Asked on February 6, 2021 by osuka_

         

Ask a Question

Get help from others!

© 2023 AnswerBun.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP