Day 36 of 100 - Pythonic Code Day 3 of 3

Pythonic Code Day 3 of 3

Day 36 of 100

This 3 day challenge got me to thinking about the Python projects I have been working on and the room for improvement that I have. The challenge was to look at your projects and code and implement some principles of pythonic code in order to improve its readability and simplicity. When I looked back at my projects and what I had written, I certainly see room for improvement but but it would be improving code and applications that I would almost immediately abandon.

Instead I decided to start a small project that was inspired by the Real Python Ideas for Intermediate Projects article I recently read. I have started to use some of what I have learned to build a command land contacts database. This project isn't so much something that fills a great need but it is a project to apply and extend what I have learned to a real project. To start, I was to apply what I have learned about classes, error handling, logging, testing, and pythonic code to the project.

So far, I was able to cobble together a small application that prompts a user to choose what they would like to do, then choose weather they want to create a new contact, search for a contact, print out a list of contacts, or quit the application. It is still in the very beginning stages, but there is something here that works, at least initially, which is enough to keep me going. contacts.py

from classes import Contact

def main():
    print_header()
    program_loop()

contact_list = []

def print_header():
    print('-' * 45 + '\n')
    print(' ' * 17 + 'CONTACTS DB' + '\n')
    print('-' * 45 + '\n')

def create_contact():
    contact = Contact.create_new_contact()
    contact_list.append(contact)

def search_contacts(contact_list):
    search_parameter = input(f'Would you like to search by [F]irst or [L]ast name: ')
    search_params = ['f', 'l']
    try:
        if search_parameter.lower() in search_params:
            # search by first letter of first name
            if search_parameter.lower() == 'f':
                search_letter = input(f'What is the first letter of the first name you are searching for: ')
                results = []
                for contact in contact_list:
                    if search_letter.lower() == contact.first_name[0].lower():
                        results.append(contact)
                if results:
                    for result in results:
                        print(result.full_name())
                else:
                    print(f'No results.')
            # TODO implement last name search
            # TODO implement full name search
            # TODO implement search by all parameters
        else:
            raise ValueError
    except:
        print('Input not recognized.')

    # Ask for search parameter - first name, last name, etc
    # Ask for exact match or first letter match
    # Ask for search term to match
    # Search contact list for search term
    # Return results to user and let the user pick which one to look at full contact info

def print_contacts(contact_list):
    contact_num = 1
    for contact in contact_list:
        print(f'Contact {contact_num}: {contact.first_name} {contact.last_name}')
        contact_num += 1

def program_loop():
    valid_inputs = ['c', 'p', 'q', 's']
    while True:
        task = input(f'What would you like to do?\n'
                     f'\t[C]reate new contact\n'
                     f'\t[P]rint contacts\n'
                     f'\t[S]earch contacts\n'
                     f'\t[Q]uit\n'
                     f'\t> ')
        try:
            if task.lower() in valid_inputs:
                if task.lower() == 'c':
                    create_contact()
                elif task.lower() == 'p':
                    print_contacts(contact_list)
                elif task.lower() == 's':
                    search_contacts(contact_list)
                elif task.lower() == 'q':
                    print('Good Bye')
                    break
            else:
                raise ValueError
        except:
            print('Please enter "C", "R", or "Q" to continue')
            continue

if __name__ == '__main__':
    main()

# TODO Database support
# TODO File i/o
# TODO Tests
# TODO Logging
# TODO Email/share contact
# TODO Look at pyinputplus for input validation
# TODO so many more things

I put the classes in a separate file. classes.py

class Contact:
    """
    A contact object
    Attributes:
        first_name(string)
        last_name(string)
        address1(string)
        address2(string)
        city(string)
        state(string)
        zip(int)
        phone(string)
        email(string)
    """
    def __init__(self, first_name, last_name,): # address1, address2, city, state, zip, phone1, email
        self.first_name = first_name
        self.last_name = last_name
        # self.address1 = address1
        # self.address2 = address2
        # self.city = city
        # self.state = state
        # self.zip = zip
        # self.phone1 = phone1
        # self.email = email

    def full_name(self):
        return f'{self.first_name} {self.last_name}'

    @classmethod
    def create_new_contact(self):
        while True:
            try:
                first_name = input('First name: ')
                if not first_name:
                    raise ValueError
                last_name = input('Last name: ')
                if not last_name:
                    raise ValueError
                return self(first_name, last_name)
            except:
                print('Invalid input. Try again.')
                # continue