-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Tiny contact book
Summary / User Story
As a command-line learner practicing Chapter 8 (Lists & Dictionaries), I want a tiny contact book where I can add, search, update, and export contacts so I can master idiomatic list/dict operations and stable iteration patterns.
What to do?
Build a console application that stores contacts as a list of dictionaries and supports add/search/update operations plus export to a plain-text file in a deterministic order.
Step-by-step (high level):
- Choose
list[dict]
as the single source of truth; fix a canonical field order. - Build a menu loop for add/search/update/export with input validation.
- Use idiomatic dict ops:
.get
,.update
, unpacking,next(..., None)
. - Ensure stable iteration: never mutate while iterating; sort consistently before showing/exporting.
- Handle I/O errors gracefully; provide clear re-prompts.
Requirements
-
Data model
- Keep all contacts in memory as
contacts: list[dict]
. - Each contact dict uses a fixed key set and order:
name
(required),phone
(optional),email
(optional),notes
(optional). - Normalize inputs: trim whitespace; store
name
in original case but use.casefold()
for comparisons.
- Keep all contacts in memory as
-
Add
- Prompt for fields; reject empty
name
. - Warn on potential duplicates (same normalized name + same phone or email); allow continue or cancel.
- Append a copy of the new dict to
contacts
.
- Prompt for fields; reject empty
-
Search
- Prompt for a free-text query.
- Case-insensitive substring match on
name
. - If query looks like digits, also match by
phone
; if it contains@
, also match byemail
. - Display results with stable indices via
enumerate(sorted_contacts)
.
-
Update
- Let the user select a contact by shown index from search results.
- For each field, show current value; empty input keeps the old value.
- Update via idiomatic patterns:
contact.update({...})
or{**contact, **changes}
. - Do not mutate the list while iterating it. If needed, iterate over a shallow copy or update by index.
-
Export
- Export to a text file (default
contacts.txt
). - Use a deterministic, stable order:
sorted(contacts, key=lambda c: c["name"].casefold())
. - Each line follows fixed field order:
Name | Phone | Email | Notes
(missing fields shown as-
). - Handle I/O errors with a friendly message (e.g., permission denied, invalid path) and return to the menu.
- Export to a text file (default
-
List (optional but recommended)
- List all contacts using the same deterministic sort and rendering as export.
-
UX
- Main loop menu:
[A]dd, [S]earch, [U]pdate, [E]xport, [Q]uit
. - Validate menu choices; re-prompt on invalid inputs.
- Keep prompts short; echo what changed after updates.
- Main loop menu:
-
Tech constraints
- Single file
main.py
, no external dependencies. - Python 3.10+ (pattern matching optional, not required).
- Single file
Acceptance Criteria
- Data structure is a list of dicts; exports use the fixed field order and are sorted case-insensitively by name.
- Add: Given a valid new contact, it appears in memory and in the next export.
- Search: Query
"ann"
returns"Ann"
and"Joanne"
; digit-only queries match phone; queries containing@
match email. - Update: Selecting a contact by shown index and entering a new phone updates only
phone
; pressing Enter foremail
leaves it unchanged. - Export: Produces
contacts.txt
with deterministic lines; attempting to export to a read-only location prints a clear error and the app continues running. - Input validation: Empty
name
is rejected with a friendly message; invalid menu choice or index re-prompts without crashing. - Iteration safety: No crashes from modifying the list while iterating (covered by tests and manual checks).
(based on materials of Learning Python by Mark Lutz — Chapter 8, Lists & Dictionaries)
Example of action
$ python3 main.py
[Tiny Contact Book]
A) Add S) Search U) Update E) Export Q) Quit
Choose: A
Name: Ann Li
Phone (optional): 555-1234
Email (optional): ann@example.com
Notes (optional): teammate
Added: Ann Li
A) Add S) Search U) Update E) Export Q) Quit
Choose: S
Query: ann
[0] Ann Li | 555-1234 | ann@example.com | teammate
1 result(s)
A) Add S) Search U) Update E) Export Q) Quit
Choose: U
Search first. Query: ann
[0] Ann Li | 555-1234 | ann@example.com | teammate
Select index: 0
Phone [555-1234]: 555-5678
Email [ann@example.com]:
Notes [teammate]: design lead
Updated: Ann Li
A) Add S) Search U) Update E) Export Q) Quit
Choose: E
File path [contacts.txt]:
Exported 1 contact(s) to contacts.txt
Example contacts.txt
:
Ann Li | 555-5678 | ann@example.com | design lead
What to do in future?
-
Persistence: load/save JSON or CSV; import from
.csv
. -
Unique IDs: add
id
per contact to disambiguate identical names. -
Delete & Undo: safe deletion and simple undo of last change.
-
Sorting & View: allow sort by name/email; pretty table view.
-
Search++: AND/OR filters, tag support via
tags: list[str]
. -
Tests (pytest):
- Add contact and assert presence in memory.
- Search
"ann"
matches expected names; digit query matches phone. - Update keeps fields on empty input and changes targeted field.
- Export yields deterministic lines given a known dataset.
- Export error path (mock failure or invalid path) handled gracefully.