TransWikia.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!

Ask a Question

Get help from others!

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