Capstone: Contact Book

Capstone: Contact Book

Build a contact book. Contacts live in a dictionary mapping each name to an inner dictionary with a "phone" and an "email" key.

Exercise

Define four functions.

  1. add_contact(book, name, phone, email) — stores the contact. If name is already in the book, raise ValueError with the message contact already exists.
  2. update_phone(book, name, phone) — replaces an existing contact's phone number. If name is not in the book, raise ValueError with the message no such contact.
  3. remove_contact(book, name) — deletes the contact. If name is not in the book, raise ValueError with the message no such contact.
  4. export_lines(book) — returns a list of strings, one per contact, sorted by name, each in the form:

<name>: <phone> <email>

book = {}
add_contact(book, "Grace", "555-0202", "grace@example.com")
add_contact(book, "Ada", "555-0101", "ada@example.com")
print(export_lines(book))

Output:

['Ada: 555-0101 ada@example.com', 'Grace: 555-0202 grace@example.com']

Run your code to see the output, then press Submit.

Hints

  • The inner dictionary is created in add_contact: {"phone": phone, "email": email}.
  • update_phone changes one key of the inner dictionary; it must not touch the email.
  • In export_lines, loop over sorted(book) to get the names in order.

Tests

import unittest


class TestContactBook(unittest.TestCase):
    def make_book(self):
        book = {}
        add_contact(book, "Grace", "555-0202", "grace@example.com")
        add_contact(book, "Ada", "555-0101", "ada@example.com")
        return book

    def test_add_stores_phone_and_email(self):
        book = self.make_book()
        self.assertEqual(book["Ada"], {"phone": "555-0101", "email": "ada@example.com"})

    def test_adding_a_duplicate_raises(self):
        book = self.make_book()
        with self.assertRaises(ValueError) as context:
            add_contact(book, "Ada", "1", "a@b.c")
        self.assertEqual(str(context.exception), "contact already exists")

    def test_update_phone_keeps_the_email(self):
        book = self.make_book()
        update_phone(book, "Ada", "555-0303")
        self.assertEqual(book["Ada"], {"phone": "555-0303", "email": "ada@example.com"})

    def test_update_phone_of_missing_contact_raises(self):
        with self.assertRaises(ValueError) as context:
            update_phone({}, "Ada", "1")
        self.assertEqual(str(context.exception), "no such contact")

    def test_remove_deletes_the_contact(self):
        book = self.make_book()
        remove_contact(book, "Grace")
        self.assertNotIn("Grace", book)

    def test_remove_missing_contact_raises(self):
        with self.assertRaises(ValueError) as context:
            remove_contact({}, "Grace")
        self.assertEqual(str(context.exception), "no such contact")

    def test_export_is_sorted_and_formatted(self):
        book = self.make_book()
        self.assertEqual(
            export_lines(book),
            [
                "Ada: 555-0101 ada@example.com",
                "Grace: 555-0202 grace@example.com",
            ],
        )

    def test_export_of_empty_book_is_empty(self):
        self.assertEqual(export_lines({}), [])
def add_contact(book, name, phone, email):
    if name in book:
        raise ValueError("contact already exists")
    book[name] = {"phone": phone, "email": email}


def update_phone(book, name, phone):
    if name not in book:
        raise ValueError("no such contact")
    book[name]["phone"] = phone


def remove_contact(book, name):
    if name not in book:
        raise ValueError("no such contact")
    del book[name]


def export_lines(book):
    lines = []
    for name in sorted(book):
        contact = book[name]
        lines.append(f"{name}: {contact['phone']} {contact['email']}")
    return lines


book = {}
add_contact(book, "Grace", "555-0202", "grace@example.com")
add_contact(book, "Ada", "555-0101", "ada@example.com")
update_phone(book, "Ada", "555-0303")
for line in export_lines(book):
    print(line)
Solution hidden. Give it a real try first.

Sign in to join the discussion.

No comments yet. Be the first to ask a question.

main.py
Console
Press Run to execute your code, or Submit to test and complete this problem.