1
\$\begingroup\$

I'm new to programming. I wrote the following code in python. It's a cmd based address book.

import os
from reportlab.pdfgen import canvas
from cryptography.fernet import Fernet

p_d_D = {}
d_D = {}
dd = {}
file_path = os.path.join(os.path.expanduser('~'), 'Desktop', str(os.path.basename(__file__)) + '__database.txt')  # The
# Location of dict database stored in the users HDD
data = {}
key = b'kXDB2gXHHwOoVC6FCwKjDhCa3JW0vRiKkHv3iBuPhx0='  # Secret key value 
# for Fernet encryption


# Loads the decrypted dict into the memory (data gets the decrypted values)
def make_eval2():
    make_eval()
    global dd
    global data
    decrypt_dict()
    data = eval(str(dd))


# A function that encrypts or decrypts dict and stores into dict db
def encrypt_dict(write=False, decrypt=False):
    l = []
    for k, v in data.items():
        for i in v:
            if decrypt is False:
                a = encrypt_decrypt(i)
            else:
                a = encrypt_decrypt(i, decrypt=True)
            l.append(a)
    ch = [l[x:x + 4] for x in range(0, len(l), 4)]
    for it in ch:
        d_D.setdefault(it[0], (it[0], it[1], it[2], it[3]))
    if write is True:
        write_read_data('w', text=str(d_D))
    global p_d_D
    p_d_D = d_D.copy()
    d_D.clear()


# Decrypts the dict data and stores into dict dd
def decrypt_dict():
    l = []
    global data
    global dd
    if len(data) < 1:
        print('No entry')
    else:
        for k, v in data.items():
            for i in v:
                a = encrypt_decrypt(i, decrypt=True)
                l.append(a)
        ch = [l[x:x + 4] for x in range(0, len(l), 4)]
        for it in ch:
            dd.setdefault(it[0], (it[0], it[1], it[2], it[3]))


# A function that does the actual enc. or dec. of provided dict values
def encrypt_decrypt(x, decrypt=False):
    global key
    f = Fernet(key)
    x_en = x.encode()
    if decrypt is False:
        a = f.encrypt(x_en).decode()
        return a
    else:
        a = f.decrypt(x_en).decode()
        return a


# Initial file maker that creates an empty dict db
def make_dict_file_to_write():
    global file_path
    if os.path.exists(file_path):
        pass
    else:
        open(file_path, 'w')


# Loads the dict db from HDD to dict data (into the memory)
def make_eval():
    global data
    x = write_read_data('r', read=True)
    if x == '':
        pass
    else:
        data = eval(x)


# A modified writing function for writing and reading to/from files
def write_read_data(mode, **kwargs):
    global file_path
    read = kwargs.get('read', False)
    text = kwargs.get('text', None)
    with open(file_path, mode) as f_to_w_read:
        if 'read' in kwargs:
            if read is True:
                return f_to_w_read.read()
            else:
                return None
        if 'text' in kwargs:
            if text is not None and mode == 'w':
                x = f_to_w_read.write(str(text))
                return x
            return None


# Returns the user given entries as encrypted values in the form of a tuple
def get_contact_details():
    a = ''
    tags = ['name', 'address', 'cell-number', 'email']

    for tag in tags:
        x = input('Please enter contact %s: ' % tag)
        a += x.lower() + ','

    name, address, cell, email = a.rstrip(',').split(',')
    h_name, h_address, h_cell, h_email = encrypt_decrypt(name), encrypt_decrypt(address), encrypt_decrypt(cell), \
                                     encrypt_decrypt(email)
    return h_name, h_address, h_cell, h_email


# Adds a new contact entry to the dict db in HDD
def add_contact():
    make_eval()
    contact = get_contact_details()
    data.setdefault(contact[0], contact)
    write_read_data('w', text=str(data))
    clear_dict()
    print('New contact added\r\n')


# Prints requested entry into a PDF file in the current folder
# It is a part of search_contact() function
def print_data(x):
    if x in data.keys():
        xx = ('Name: %s\r\nAddress: %s\r\nCell-number : %s\r\nEmail: %s\r\n' % (data[x][0].capitalize(), data[x][1].
                                                                            capitalize(),
                                                                            data[x][2].capitalize(),
                                                                            data[x][3].lower()))
        print(xx)
        file_name = '%s\'s contact information' % x.capitalize()
        c = canvas.Canvas(file_name + '.pdf', pagesize='A4')
        t = c.beginText()
        t.setFont('Times-Bold', 12)
        t.setTextOrigin(30, 700)
        t.textLines(file_name + ' :\r\n')
        t.textLines(xx)
        c.drawText(t)
        c.showPage()
        c.save()
        print('Document will be printed after quitting\r\n')


# Prints all the entries of the dict db into readable format
def print_all():
    make_eval()
    if len(data) < 1:
        print('No entry')
    else:
        encrypt_dict(decrypt=True)
        global p_d_D
        for k, v in p_d_D.items():
            print('Name: %s\r\nAddress: %s\r\nCell-number : %s\r\nEmail: %s\r\n' % (v[0].capitalize(),
                                                                                v[1].capitalize(),
                                                                                v[2], v[3].lower()))
    p_d_D.clear()


# Allows a requested entry to be printed out in the PDF format
def search_contact():
    make_eval2()
    search_item = input('Enter the full name of the person you wish to search: \r\n').lower()
    if search_item in data.keys():
        print_data(search_item)
        clear_dict()
    else:
        print('No match found\r\n')


# Part of edit_contact() function, which provides different editing choices to the user
def choice_edit():
    while True:
        choiceEdit = input('Press "A" to change address\nPress "C" '
                       'to change cell-number\nPress "E" to change email\nPress "Q" '
                       'to quit without editing\n').lower()
        if choiceEdit == 'a' or choiceEdit == 'c' or choiceEdit == 'e' or choiceEdit == 'q':
            return choiceEdit
        print('Not correct option\r\n')


# Part of edit_contact() function
def do_it_again():
    while True:
        try:
            doAgain = int(input('Press 1 to edit again\nPress 2 to save and quit\r\n'))
            if doAgain == 1 or doAgain == 2:
                return doAgain
            print('Not correct option\r\n')
        except ValueError:
            print('Select an integer\r\n')


# Part of edit_contact() function
def print_try(x=None):
    xx = input('Enter new %s\r\n' % x).lower()
    return xx


# Stores the updated dict to the dict db
def update(f, c):
    x = tuple(f)  # Converts the list X into a tuple
    data.update({(c, x)})  # Updates data dict
    encrypt_dict(write=True)


# Part of edit_contact() function which gives the option to store edited data or re-edit a requested entry
def next_gen(list_x, edit_pointer, edit_name, print_call=''):
    list_x[edit_pointer] = print_try(x=print_call)
    update(list_x, edit_name)
    doooAgain = do_it_again()
    if doooAgain == 1:
        edit_contact()
    if doooAgain == 2:
        update(list_x, edit_name)
        clear_dict()


# Edits an stored entry on request
def edit_contact():
    make_eval2()  # Decrypts the dict to readable format
    var_edit_name = input('Enter the contact name that you with to edit: \r\n').lower()
    if var_edit_name in data.keys():
        z = list(data[var_edit_name])  # Converts the tuple into a list to allow edits
        cho_edit = choice_edit()
        if cho_edit == 'a':
            next_gen(z, 1, var_edit_name, print_call='address')
        elif cho_edit == 'c':
            next_gen(z, 2, var_edit_name, print_call='cell-number')
        elif cho_edit == 'e':
            next_gen(z, 3, var_edit_name, print_call='email')
        elif cho_edit == 'q':
            update(z, var_edit_name)
            clear_dict()
        else:
            print('Please select a correct option\r\n')
            edit_contact()
    else:
        print('No such name found\r\n')


# Clears the memories of all the dicts
def clear_dict():
    global dd, data, p_d_D, d_D
    dd.clear()
    data.clear()
    p_d_D.clear()
    d_D.clear()


# Deletes an entry
def delete_contact():
    make_eval2()  # Decrypts the dict to readable format
    var_del_name = input('Enter the contact name that you with to delete: \r\n').lower()
    if var_del_name in data.keys():
        data.pop(var_del_name, None)
        encrypt_dict(write=True)  # Encrypts the dict to unreadable format and stores it in the dict database
        clear_dict()  # Clears the memories of all the dicts
    else:
        print('No such name found\r\n')


# Initially checks if the dict db is empty or not
def initial_emptyness_checker():
    make_eval()
    if len(data) < 1:
        print('Address book is currently empty\r\n')
    else:
        return True


# Allows to quit from the app
def quit_here():
    make_eval()
    write_read_data('w', text=str(data))
    quit()


# Provides options to the user to choose a set of action
def option_switcher():
    options = {
        1: add_contact,
        2: search_contact,
        3: delete_contact,
        4: edit_contact,
        5: print_all,
        6: quit_here
}

    xx = ['Add contract', 'Search', 'Delete', 'Edit', 'Print all entries', 'Quit']
    c = 1

    for i in xx:
        print('Option %d: %s' % (c, i))
        c += 1

    try:
        select = int(input('\nPlease select an option \r\n'))
        if 1 <= select < 7:
            options[select]()
        else:
            print('Options 1-6\r\n')
    except ValueError:
        print('Invalid option\r\n')
        option_switcher()


def main():
    print('Welcome to AddressBookEXtREM V1 by AJ\r\n')
    make_dict_file_to_write()
    initial_emptyness_checker()
    while True:
        option_switcher()


if __name__ == '__main__':
    main()

The above app allows a user to make contact lists and store them in the hard disk. Moreover, it provides security to the stored database file via encryption. User can anytime update or delete or edit stored contacts. Moreover, it provides the user to print out the searched item into a PDF file. The code is pretty long. I hope someone could suggest me to make it shorter or more efficient.

\$\endgroup\$
  • \$\begingroup\$ one thing that immediately jumps out at me: using eval(). use JSON to encode and decode the dict instead, otherwise someone can put arbitrary python code in the file and have it run. docs.python.org/2/library/json.html \$\endgroup\$ – dn3s Aug 13 '17 at 16:09
1
\$\begingroup\$

Some small improvements:

Using docstrings in Python has more advantages than using comments - e. g. instead of

# Adds a new contact entry to the dict db in HDD
def add_contact():
    make_eval()
    ...
    ...

you may consider

def add_contact():
    '''Adds a new contact entry to the dict db in HDD'''
    make_eval()
    ...
    ...

In line 177:

    if search_item in data.keys():

you may use a simpler form

    if search_item in data:

In your option_switcher() function you created the options dictionary, so in line 312 you may instead of

    if 1 <= select < 7:

use more flexible (for the case of updating your options dictionary)

    if select in options:

Instead of

    if cho_edit == 'a':
        next_gen(z, 1, var_edit_name, print_call='address')
    elif cho_edit == 'c':
        next_gen(z, 2, var_edit_name, print_call='cell-number')
    elif cho_edit == 'e':
        next_gen(z, 3, var_edit_name, print_call='email')

would be nicer using a dictionary, e. g.

    targets = {'a': (1, 'address'), 'c': (2, 'cell-number'), 'e': (3, 'email')}

    if cho_edit in targets:
        num, target = targets[cho_edit]
        next_gen(z, num, var_edit_name, print_call=target)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.