Skip to content

Tiny contact book #5

@wak3upalex

Description

@wak3upalex

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):

  1. Choose list[dict] as the single source of truth; fix a canonical field order.
  2. Build a menu loop for add/search/update/export with input validation.
  3. Use idiomatic dict ops: .get, .update, unpacking, next(..., None).
  4. Ensure stable iteration: never mutate while iterating; sort consistently before showing/exporting.
  5. 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.
  • 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.
  • 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 by email.
    • 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.
  • 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.
  • Tech constraints

    • Single file main.py, no external dependencies.
    • Python 3.10+ (pattern matching optional, not required).

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 for email 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.

Metadata

Metadata

Assignees

Labels

help wantedExtra attention is needed

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions