2
\$\begingroup\$

I'm trying my hand at an address book and the goal is for it to have a bit more of error prevention. For instance, for the key "phone" to have 10 digits, and not allow alpha characters at section 4. I believe I have formatted this properly to the best of my ability. I understand this is very elementary as I'm new to any sort of code.

My goal is to call this properly. I originally was using defs for name, phone, email but this way seemed more concise. I'm unsure how my written code can formatted to apply to the 'phone' key in the dictionary.

I certainly would like to hear opinions of how I could make this into a class with the name, phone, and email being defs as that was my original goal.

def main():

    # Creates an empty list of contacts
    contactlist = collections.OrderedDict()
    loops = True

    # While loop used for options, it loops for user input until sequence is over
    while loops == True:

        # Prints option list for the user
        print ('Greetings user! Lets make an addressbook!', '\n', '\n', 'Would you like to: ', '\n', '1.) Add a New Contact', '\n', '2.) List All Contacts', '\n', '3.) Search Contacts', '\n','4.) Edit A Contact', '\n', '5.) Delete A Contact', '\n', '6.) Quit Program')

        # Asks for users input from 1-6
        userInput = input("Select an option: ")

        # 1 : Add a new contact to list, also start of program if no file is made
        if userInput == "1":
            contactname = input( "Enter name: ")

            contactlist[contactname] = {'name': contactname, 'phone': input("Enter phone number: "), 'email': input("Enter email: ").lower()}

            json.dump(contactlist, open('contacts.txt','w'))

            # Error Checking

            #phone number
            '''[k]['phone']
            while True:
            try:
                if len(phone) != 10:
                    print('Not 10 digits')
                else:
                    phone = (phone[:3]+'-'+phone[3:6]+'-'+phone[6:9])
                    return phone
            except:
                print("Not all numbers inputed are digits")

            '''
            print ("Contact Added!")


        #  2 : list of contacts in addressbook
        elif userInput == "2":
            print ('\n', "Listing Contacts...")
            try:
                contactlist = json.load(open('contacts.txt','r'))
                name_keys = list(contactlist.keys())
            except:
                contacts = {}

            print ("%-30s %-30s %-30s" % ('NAME','PHONE','EMAIL'))
            #better formatting than using tab spaces and keeps items in a predetermined space apart from eachother

            for k in name_keys:
                print ("%-30s %-30s %-30s" % (contactlist[k]['name'], contactlist[k]['phone'], contactlist[k]['email']))
                #same idea as fotmatting above for each of the dict values

        #  3 : search through contacts!
        elif userInput == "3":
            print ('\n', "Searching Contacts...")
            search = input("Please enter name (case sensitive): ")

            try:
                contactlist = json.load(open('contacts.txt','r'))
            except:
                contactlist = []

            try:
                print ("%-30s %-30s %-30s" % (contactlist[search]['name'], contactlist[search]['phone'], contactlist[search]['email']))
            except KeyError: #error reporting- whenever a dict() object is requested & key is not in the dict.
                print ("Not Found")


##########################################################################
        #4 : Edit a contact!
        elif userInput == "4":
            print ('\n', "Editing Contact...")
            search_edit = input("Please enter name: ")



            try:
                contactlist.pop(contactname)
                json.dump(contactlist, open('contacts.txt','w'))
            except KeyError: #error reporting- whenever a dict() object is requested & key is not in the dict.
                print ('\n', "Contact Not Found")


        #  5 : Delete contact
        elif userInput == "5":
            print ("Deleting Contact...")
            contactname = input("Enter Contact Name: ")
            contacts = json.loads(open('contacts.txt').read())

            try:
                contacts.pop(contactname)
                json.dump(contacts, open('contacts.txt','w'))
            except KeyError: #error reporting- whenever a dict() object is requested & key is not in the dict.
                print ('\n', "Contact Not Found")



        # 6 : end program
        elif userInput == "6":
            print ('\n', "Ending Contact Book.")
            print('Have a nice day!')
            loop = False
        else:
            print ("Invalid Input! Try again.")


main()
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$
loops = True
while loops == True:
    …
    if …:
        loops = False

You can simplify this logic using break:

while True:
    …
    if …:
        break

contactlist = collections.OrderedDict()

Well, is it a list or a dict? The naming is at least misleading, try to use contacts or something alike. However, there are some other places with contactlist = json.load(…), contactlist = {} or even contactlist = []; which make it either a regular dict or a list. All of that is very confusing.

You should simplify the logic by loading your contacts at the beginning of the function and use that all along:

try:
    with open('contact.txt') as f:
        contacts = json.loads(f)
except (FileNotFoundError, json.decoder.JSONDecodeError):
    # File not found or empty, use a default container
    contacts = {}

You should note the use of with in this snippet: each time you open a file, you should close it afterwards to release associated resources. Using with ensures the file will be closed whatever happens (success or exception raised).

This will simplify #2, #3 and #5 as the contacts are already loaded.


print ('Greetings user! …')

You can make this kind of lines much more readable by using multi-line strings:

def main():
    try:
        with open('contact.txt') as f:
            contacts = json.loads(f)
    except (FileNotFoundError, json.decoder.JSONDecodeError):
        # File not found or empty, use a default container
        contacts = {}

    while True:
        print('''Greetings user! Let's make an addressbook!

Would you like to:
 1.) Add a New Contact
 2.) List All Contacts
 3.) Search Contacts
 4.) Edit A Contact
 5.) Delete A Contact
 6.) Quit Program''')
        …

Speaking of which: multi-line strings are not comments. They are just random noise in the code. You wouldn't put a random integer in your code, so don't do it with random strings.


You can simplify some of your logic by using helper functions. Printing informations on a contact should be done in #2 and #4; asking new informations about a contact should be done in #1 and #4. So it's worth using functions to remove redundancy:

def print_contact(contact_info):
    print("{0[name]:<30} {0[phone]:<30} {0[email]:<30}".format(contact_info))

It uses the new format syntax which is the prefered way of doing in Python 3.

def create_contact(name=None):
    if name is None:  # Name not provided, so we need to ask for it
        name = input("Enter name: ")
    phone = input("Enter phone number: ")
    email = input("Enter email: ").lower()
    # Do whatever validation you like on `phone` or `email`
    return {'name': name, 'phone': phone, 'email': email}

You could also extend this function by having a "validation" phase and raising an exception in case of error, you’ll then need to account for that in your calling code:

class NotAPhoneNumberException(ValueError):
    pass

def print_contact(contact_info):
    print("{0[name]:<30} {0[phone]:<30} {0[email]:<30}".format(contact_info))

def create_contact(name=None):
    if name is None:  # Name not provided, so we need to ask for it
        name = input("Enter name: ")
    phone = input("Enter phone number: ")
    email = input("Enter email: ").lower()
    # Do whatever validation you like on `phone` or `email`
    # and `raise` an exception if need be
    return {'name': name, 'phone': phone, 'email': email}

def main():
    try:
        with open('contact.txt') as f:
            contacts = json.loads(f)
    except (FileNotFoundError, json.decoder.JSONDecodeError):
        # File not found or empty, use a default container
        contacts = {}

    while True:
        print('''Greetings user! Let's make an addressbook!

Would you like to:
 1.) Add a New Contact
 2.) List All Contacts
 3.) Search Contacts
 4.) Edit A Contact
 5.) Delete A Contact
 6.) Quit Program''')

        choice = input("Select an option: ")

        if choice == "1":
            try:
                new_contact = create_contact()
            except NotAPhoneNumberException:
                print("The phone number entered is invalid, creation aborted!")
            else:
                contacts[new_contact['name']] = new_contact
                with open('contact.txt', 'w') as f:
                    json.dump(contacts, f)
        elif choice == "2":
            …

main()

When having code at top-level like that, it is good practice to use the if __name__ == '__main__': clause.

You should also give your function a better name which describe what the function does. Lastly, it would be much more usefull if the function take the filename ('contact.txt') as a parameter.


All in all, you code can become:

import json


class NotAPhoneNumberException(ValueError):
    pass


def save_contacts(contacts, filename):
    with open(filename, 'w') as f:
        json.dump(contacts, f)


def print_contact(contact_info):
    print("{0[name]:<30} {0[phone]:^30} {0[email]:>30}".format(contact_info))


def create_contact(name=None):
    if name is None:  # Name not provided, so we need to ask for it
        name = input("Enter name: ")
    phone = input("Enter phone number: ")
    email = input("Enter email: ").lower()

    if len(phone) != 10:  # Very simple validation
        raise NotAPhoneNumberException

    return {'name': name, 'phone': phone, 'email': email}


def address_book(filename):
    try:
        with open(filename) as f:
            contacts = json.loads(f)
    except (FileNotFoundError, json.decoder.JSONDecodeError):
        # File not found or empty, use a default container
        contacts = {}

    while True:
        print('''Greetings user! Let's make an addressbook!

Would you like to:
 1.) Add a New Contact
 2.) List All Contacts
 3.) Search Contacts
 4.) Edit A Contact
 5.) Delete A Contact
 6.) Quit Program''')

        choice = input("Select an option: ")

        if choice == "1":
            try:
                new_contact = create_contact()
            except NotAPhoneNumberException:
                print("The phone number entered is invalid, creation aborted!")
            else:
                contacts[new_contact['name']] = new_contact
                save_contacts(contacts, filename)

        elif choice == "2":
            print_contact({'name': 'NAME', 'phone': 'PHONE', 'email': 'EMAIL'})
            for contact in contacts.values():
                print_contact(contact)

        elif choice == "3":
            search = input("Please enter name (case sensitive): ")
            try:
                print_contact(contacts[search])
            except KeyError:
                print("Contact not found")

        elif choice == "4":
            search = input("Please enter name (case sensitive): ")
            try:
                print_contact(contacts[search])
            except KeyError:
                print("Contact not found")
            else:
                try:
                    contacts[search] = create_contact(search)
                except NotAPhoneNumberException:
                    print("Invalid phone number. Contact was not edited!")
                else:
                    save_contacts(contacts, filename)

        elif choice == "5":
            search = input("Please enter name (case sensitive): ")
            try:
                contacts.pop(search)
            except KeyError:
                print("Contact not found")
            else:
                save_contacts(contacts, filename)

        elif choice == "6":
            print("Ending Contact Book.\nHave a nice day!")
            break

        else:
            print("Invalid Input! Try again.")


if __name__ == "__main__":
    address_book('contact.txt')

You could simplify further by using a dictionary mapping to functions instead of all those elif but let's call it a day.

\$\endgroup\$
0
0
\$\begingroup\$

I would try using others code in this endeavor like the phonenumbers module for verification and parsing/printing. There are example code out there which can help your formatting. You want to break up your code into manageable chunks so you could put these into libraries if this gets to be large. e.g. https://retrosnob.wordpress.com/2014/10/14/command-line-contact-management-application-in-python/

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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