Lesson 5: Contact Card Validator — Real-World Integration Project
You've learned four essential skills in this chapter: creating and manipulating strings, formatting output with f-strings, and safely converting between types. Now it's time to apply everything to solve a real problem—a Contact Card Validator that cleans, validates, and formats contact information.
Every application needs to collect user data: registration forms, profile pages, contact forms, CRM systems. Messy input like " JOHN DOE ", invalid emails, or non-numeric ages cause problems. Your job as a developer is to clean and validate this data before storing it.
This project is different from exercises. You'll start by describing what the program should do, then build it with AI as your partner, test it with realistic data, and validate that it works correctly. This is how real programming works: you solve a problem someone has, not just demonstrate concepts.
By the end of this lesson, you'll have built a practical tool that validates contact information using all Chapter 16 concepts—string methods, f-strings, type casting, and validation—skills you'll use in every application you build.
Phase 1: Program Design — Describing Intent First
Before writing code, the most important step is clarifying what problem you're solving. This is the intent-first approach that separates professional developers from code copiers.
What is the Contact Card Validator?
The Contact Card Validator is a practical tool that solves a real problem: cleaning and validating messy contact information.
The Problem: Users enter contact data inconsistently:
- Names:
" john doe ","JANE SMITH","bob_jones" - Emails:
"user@example","invalid.com","@nouser" - Phone:
"(555) 123-4567","555.123.4567","5551234567" - Age:
"25","twenty-five","99999"
The Solution: A validator that:
-
Cleans names (Lesson 2: String Methods):
- Removes extra whitespace:
strip() - Fixes capitalization:
title()for proper names - Replaces underscores with spaces:
replace("_", " ")
- Removes extra whitespace:
-
Validates emails (Lesson 2: String Methods):
- Checks for
@symbol:find("@") - Ensures not empty after cleaning:
strip() - Basic format validation
- Checks for
-
Formats phone numbers (Lesson 2: String Methods):
- Removes formatting characters:
replace("(", "").replace(")", "").replace("-", "").replace(".", "").replace(" ", "") - Validates length (10 digits)
- Displays in standard format
- Removes formatting characters:
-
Validates age (Lesson 4: Type Casting):
- Converts string to integer:
int(age_str) - Validates reasonable range (0-120)
- Handles invalid input gracefully
- Converts string to integer:
-
Displays formatted contact card (Lesson 3: F-Strings):
- Shows cleaned data in professional format
- Indicates validation status for each field
💬 AI Colearning Prompt
"I'm building a contact validator. What fields should I validate? What are common data quality issues with names, emails, and phone numbers? How should I handle invalid data?"
This conversation helps you understand the problem domain before coding.
Design Intent — User Story
User Story: As a developer building a registration form, I need to clean and validate contact information so that my database contains high-quality data.
User Interaction Flow:
1. Program starts → Explains what it does
2. Asks for name → User enters messy name
3. Cleans name → Shows original vs. cleaned
4. Asks for email → User enters email
5. Validates email → Shows valid/invalid with reason
6. Asks for phone → User enters phone
7. Formats phone → Shows cleaned number
8. Asks for age → User enters age
9. Validates age → Converts and validates range
10. Displays contact card → Shows all cleaned data formatted nicely
🎓 Instructor Commentary
In real programming, you're solving problems for users. The Contact Card Validator solves data quality problems every application faces. You're not just learning string methods—you're learning when to use
strip()(always for user input),title()(for names), andfind()(for validation). This is professional development: understanding the problem before writing a single line of code.
Phase 2: Building Your Validator — AI-Guided Implementation
Now let's build the Contact Card Validator. This code demonstrates all Chapter 16 concepts solving a real problem.
Code Example 5.1: Basic Contact Card Validator
This is a working version that validates and formats contact information using all Lessons 1-4 concepts:
# Contact Card Validator — Real-World Application
# Demonstrates: string cleaning, validation, f-strings, type casting
print("=== Contact Card Validator ===")
print("Clean and validate contact information\n")
# Step 1: Name Validation (Lessons 1-2: Strings and Methods)
print("STEP 1: NAME VALIDATION")
print("-" * 50)
name_input: str = input("Enter full name: ")
# Clean the name (Lesson 2: String Methods)
cleaned_name: str = name_input.strip() # Remove leading/trailing whitespace
cleaned_name = cleaned_name.replace("_", " ") # Replace underscores with spaces
cleaned_name = cleaned_name.title() # Capitalize properly (Title Case)
# Display results (Lesson 3: F-Strings)
print(f"\nOriginal name: '{name_input}'")
print(f"Cleaned name: '{cleaned_name}'")
print(f"Length: {len(cleaned_name)} characters")
# Validation status
if len(cleaned_name) > 0:
print("✓ Valid name")
else:
print("✗ Invalid: Name cannot be empty")
# Step 2: Email Validation (Lesson 2: String Methods)
print(f"\nSTEP 2: EMAIL VALIDATION")
print("-" * 50)
email_input: str = input("Enter email address: ")
# Clean email
cleaned_email: str = email_input.strip().lower() # Remove whitespace, lowercase
# Basic validation: must contain @ and not be empty
at_position: int = cleaned_email.find("@")
print(f"\nOriginal email: '{email_input}'")
print(f"Cleaned email: '{cleaned_email}'")
if at_position > 0 and at_position < len(cleaned_email) - 1:
# @ exists and is not first or last character
print(f"✓ Valid email (@ found at position {at_position})")
else:
print("✗ Invalid: Email must contain @ symbol (not at start or end)")
# Step 3: Phone Number Formatting (Lesson 2: String Methods)
print(f"\nSTEP 3: PHONE NUMBER FORMATTING")
print("-" * 50)
phone_input: str = input("Enter phone number: ")
# Remove all formatting characters (Lesson 2: replace)
cleaned_phone: str = phone_input.strip()
cleaned_phone = cleaned_phone.replace("(", "")
cleaned_phone = cleaned_phone.replace(")", "")
cleaned_phone = cleaned_phone.replace("-", "")
cleaned_phone = cleaned_phone.replace(".", "")
cleaned_phone = cleaned_phone.replace(" ", "")
print(f"\nOriginal phone: '{phone_input}'")
print(f"Digits only: '{cleaned_phone}'")
# Validate length and format nicely
if cleaned_phone.isdigit() and len(cleaned_phone) == 10:
# Format as (XXX) XXX-XXXX
formatted_phone: str = f"({cleaned_phone[0:3]}) {cleaned_phone[3:6]}-{cleaned_phone[6:10]}"
print(f"Formatted phone: {formatted_phone}")
print("✓ Valid phone number")
else:
print("✗ Invalid: Phone must be exactly 10 digits")
# Step 4: Age Validation (Lesson 4: Type Casting)
print(f"\nSTEP 4: AGE VALIDATION")
print("-" * 50)
age_input: str = input("Enter age: ")
# Clean and validate (Lesson 4: Type Casting with validation)
cleaned_age: str = age_input.strip()
if cleaned_age.isdigit():
age: int = int(cleaned_age)
print(f"\nString: '{cleaned_age}'")
print(f"Integer: {age} (type: {type(age).__name__})")
# Validate reasonable age range
if 0 <= age <= 120:
print(f"✓ Valid age")
# Boolean truthiness demonstration
is_adult: bool = age >= 18
print(f"Is adult (18+): {is_adult}")
else:
print("✗ Invalid: Age must be between 0 and 120")
else:
print(f"✗ Invalid: '{age_input}' is not a valid number")
print("Reason: Age must contain only digits")
# Step 5: Display Contact Card (Lesson 3: F-Strings)
print(f"\n{'=' * 50}")
print("CONTACT CARD")
print("=" * 50)
# Only show card if we have valid data
if len(cleaned_name) > 0:
print(f"Name: {cleaned_name}")
print(f"Email: {cleaned_email}")
if cleaned_phone.isdigit() and len(cleaned_phone) == 10:
print(f"Phone: {formatted_phone}")
else:
print(f"Phone: (invalid)")
if cleaned_age.isdigit() and 0 <= int(cleaned_age) <= 120:
print(f"Age: {age} years old")
else:
print(f"Age: (invalid)")
print("=" * 50)
print("Contact validated successfully!")
else:
print("Error: Cannot create contact card with invalid name")
print("\n=== Validation Complete ===")
What This Code Demonstrates:
- Lesson 1 (String Fundamentals): Using
len(), string indexing for phone formatting (cleaned_phone[0:3]), type validation withtype() - Lesson 2 (String Methods):
strip()for all input,title()for names,lower()for emails, multiplereplace()calls for phone cleaning,find()for email validation,isdigit()for number validation - Lesson 3 (F-Strings): Formatting output throughout (
f"Name: {cleaned_name}"), formatting phone number with slicing in f-string - Lesson 4 (Type Casting): Converting age string to integer with validation, checking valid ranges, boolean logic for adult check
Real-World Application:
This code solves actual problems:
- Names: Handles
" john doe "→"John Doe","bob_smith"→"Bob Smith" - Emails: Validates basic format, cleans whitespace, normalizes to lowercase
- Phone: Accepts any format → converts to standard
(555) 123-4567 - Age: Validates numeric input and reasonable ranges
Specification Reference & Validation:
- Spec: Contact validator must clean and validate contact data using Lessons 1-4 concepts
- AI Prompt Used: (The above code implements the design intent from Phase 1)
- Validation Steps:
- Run with messy name (
" john DOE ") → Cleans to"John Doe" - Run with valid email (
"[email protected]") → Validates successfully - Run with formatted phone (
"(555) 123-4567") → Extracts digits and reformats - Run with valid age (
"25") → Converts and validates range - Run with invalid data → Shows helpful error messages
- Run with messy name (
Code Example 5.2: Enhanced Validator with Functions and Error Tracking
This version adds organization, better error handling, and validation summary:
# Contact Card Validator — Enhanced Version
# Demonstrates improved code organization and comprehensive validation tracking
def clean_name(raw_name: str) -> tuple[str, bool]:
"""Clean and validate name. Returns (cleaned_name, is_valid)."""
cleaned: str = raw_name.strip().replace("_", " ").title()
is_valid: bool = len(cleaned) > 0
return cleaned, is_valid
def validate_email(raw_email: str) -> tuple[str, bool, str]:
"""Validate email. Returns (cleaned_email, is_valid, message)."""
cleaned: str = raw_email.strip().lower()
at_pos: int = cleaned.find("@")
if at_pos > 0 and at_pos < len(cleaned) - 1:
return cleaned, True, "Valid email format"
else:
return cleaned, False, "Email must contain @ (not at start/end)"
def format_phone(raw_phone: str) -> tuple[str, bool, str]:
"""Format and validate phone. Returns (formatted_phone, is_valid, message)."""
# Remove all formatting
digits: str = raw_phone.strip()
digits = digits.replace("(", "").replace(")", "")
digits = digits.replace("-", "").replace(".", "").replace(" ", "")
if digits.isdigit() and len(digits) == 10:
formatted: str = f"({digits[0:3]}) {digits[3:6]}-{digits[6:10]}"
return formatted, True, "Valid phone number"
else:
return digits, False, f"Phone must be 10 digits (got {len(digits)})"
def validate_age(raw_age: str) -> tuple[int | None, bool, str]:
"""Validate age. Returns (age_int, is_valid, message)."""
cleaned: str = raw_age.strip()
if not cleaned.isdigit():
return None, False, "Age must be a number"
age: int = int(cleaned)
if 0 <= age <= 120:
return age, True, "Valid age"
else:
return age, False, "Age must be between 0 and 120"
# Main program
print("=== Contact Card Validator (Enhanced) ===\n")
# Collect all input
name_raw: str = input("Enter full name: ")
email_raw: str = input("Enter email: ")
phone_raw: str = input("Enter phone: ")
age_raw: str = input("Enter age: ")
# Validate all fields
name_clean, name_valid = clean_name(name_raw)
email_clean, email_valid, email_msg = validate_email(email_raw)
phone_formatted, phone_valid, phone_msg = format_phone(phone_raw)
age_value, age_valid, age_msg = validate_age(age_raw)
# Display validation results
print(f"\n{'=' * 50}")
print("VALIDATION RESULTS")
print("=" * 50)
print(f"\nName: '{name_raw}' → '{name_clean}'")
print(f" {'✓ Valid' if name_valid else '✗ Invalid'}")
print(f"\nEmail: '{email_raw}' → '{email_clean}'")
print(f" {'✓' if email_valid else '✗'} {email_msg}")
print(f"\nPhone: '{phone_raw}' → '{phone_formatted}'")
print(f" {'✓' if phone_valid else '✗'} {phone_msg}")
print(f"\nAge: '{age_raw}' → {age_value if age_value else '(invalid)'}")
print(f" {'✓' if age_valid else '✗'} {age_msg}")
# Display contact card only if all valid
all_valid: bool = name_valid and email_valid and phone_valid and age_valid
print(f"\n{'=' * 50}")
if all_valid:
print("CONTACT CARD — ALL FIELDS VALID")
print("=" * 50)
print(f"Name: {name_clean}")
print(f"Email: {email_clean}")
print(f"Phone: {phone_formatted}")
print(f"Age: {age_value} years old")
if age_value and age_value >= 18:
print(" (Adult)")
print("=" * 50)
print("✓ Contact validated successfully!")
else:
print("VALIDATION FAILED")
print("=" * 50)
print("Fix the errors above and try again.")
print("\n=== Validation Complete ===")
Why This Version Is Better:
- Organization: Functions handle one validation task each (single responsibility)
- Reusability: Each validator can be called independently or reused in other programs
- Comprehensive Feedback: Returns both cleaned data AND validation status with messages
- Type Hints: Uses advanced type hints (
tuple[str, bool],int | None) showing professional patterns - Validation Summary: Shows all results together, not step-by-step (better UX)
- All-or-Nothing: Contact card only displays if ALL fields valid (real-world requirement)
Real-World Pattern:
This is how professional applications validate forms:
- Collect all input first
- Validate each field independently
- Show all errors at once (not one-by-one)
- Only proceed if everything is valid
Note: Functions and tuples are from Chapter 20. This example shows how your validator could evolve; focus on understanding the validation patterns, not memorizing function syntax.
Phase 3: Testing Your Validator — Real-World Scenarios
Now you validate that your contact validator works correctly with realistic data. Testing means running your code with messy, invalid, and edge-case inputs—just like real users provide.
🚀 CoLearning Challenge
Ask your AI Co-Teacher:
"I've built a Contact Card Validator. Help me test it with these realistic scenarios:
- Name with extra whitespace and wrong capitalization
- Email missing @ symbol or domain
- Phone number in different formats (dashes, parentheses, dots)
- Age as text instead of number, or unrealistic ages (like 999)
- All fields empty
For each, what should my program output? What makes good validation?"
Expected Outcome: You understand how to validate real user input and predict edge cases your program should handle gracefully.
Test Cases You Should Try
Test 1: Messy Name
Enter full name: john_doe
Expected: Cleans to "John Doe" (strips whitespace, replaces underscore, title case)
Validation: ✓ Valid name
Test 2: Valid Contact
Name: Sarah Wilson
Email: [email protected]
Phone: (555) 123-4567
Age: 28
Expected: All fields validate, contact card displays, adult status shown
Test 3: Invalid Email
Email: userexample.com (missing @)
Expected: Shows "✗ Email must contain @ (not at start/end)"
Email: @example.com (@ at start)
Expected: Shows same error (invalid format)
Test 4: Phone Number Variations
Phone: 555-123-4567 → Formatted as (555) 123-4567 ✓
Phone: (555) 123-4567 → Formatted as (555) 123-4567 ✓
Phone: 555.123.4567 → Formatted as (555) 123-4567 ✓
Phone: 5551234567 → Formatted as (555) 123-4567 ✓
Phone: 123-456 → ✗ Invalid (only 6 digits)
Phone: 555-123-456a → ✗ Invalid (contains letter)
Test 5: Age Validation
Age: 25 → ✓ Valid age, Adult
Age: 17 → ✓ Valid age, (not adult)
Age: 0 → ✓ Valid age (newborn)
Age: 150 → ✗ Invalid: Age must be between 0 and 120
Age: twenty → ✗ Invalid: Age must be a number
Age: -5 → ✗ Invalid: Age must be a number (negative fails isdigit())
Test 6: Edge Case - All Empty
Name: (press Enter)
Email: (press Enter)
Phone: (press Enter)
Age: (press Enter)
Expected: All fields fail validation, contact card not displayed, clear error messages
Test 7: Realistic Messy Data
Name: BOB_SMITH (extra spaces, underscores, all caps)
Email: [email protected] (uppercase)
Phone: 1-555-123-4567 (includes country code digit)
Age: 30 (whitespace)
Expected behavior:
- Name: Cleans to "Bob Smith" ✓
- Email: Cleans to "[email protected]" ✓
- Phone: ✗ Invalid (11 digits instead of 10)
- Age: Cleans to "30" ✓
✨ Teaching Tip
Real applications face messy data constantly. Your validator must handle:
- Whitespace: Users add spaces everywhere (
" john ")- Capitalization: Users type ALL CAPS or all lowercase
- Format variations: Phone numbers come in dozens of formats
- Invalid input: Users ignore instructions ("twenty" instead of "20")
- Edge cases: Empty fields, extreme values
When your program doesn't work as expected, ask your AI: "Why did this fail? What validation am I missing?" This is professional development—anticipating problems before they happen.
Phase 4: Reflection — Connecting Back to Chapter 16
The most important part of this project is understanding how all Chapter 16 concepts solve a real problem together.
What Lessons 1-4 Did Your Validator Use?
String Fundamentals (Lesson 1)
Your validator uses:
- String creation: All contact fields start as strings from user input
- String length:
len(cleaned_name)validates non-empty names,len(cleaned_phone)checks 10 digits - String indexing:
cleaned_phone[0:3]extracts area code for formatting - Immutability understanding: Each cleaning operation returns a new string
String Methods (Lesson 2)
Your validator applies (most critical lesson!):
strip(): Applied to ALL user input first (professional best practice)title(): Fixes name capitalization ("john doe"→"John Doe")lower(): Normalizes emails ("[email protected]"→"[email protected]")replace(): Cleans phone numbers (multiple calls to remove()-.characters)find(): Validates email format (checks for@symbol)isdigit(): Validates phone digits and age numbers
Key Insight: Every method solves a specific real-world problem. You now know WHEN to use each method, not just HOW.
F-String Formatting (Lesson 3)
Your validator uses:
- Variable embedding:
f"Name: {cleaned_name}"displays contact fields - Expressions:
f"Phone must be 10 digits (got {len(digits)})"shows why validation failed - Conditional formatting:
f"{'✓ Valid' if name_valid else '✗ Invalid'}"shows status - String slicing in f-strings:
f"({cleaned_phone[0:3]}) {cleaned_phone[3:6]}-{cleaned_phone[6:10]}"formats phone
Type Casting (Lesson 4)
Your validator demonstrates:
- String to integer:
int(cleaned_age)converts age for range validation - Validation before conversion:
cleaned_age.isdigit()prevents crashes - Boolean logic:
age >= 18determines adult status,all_valid = name_valid and email_valid and phone_valid and age_validchecks all fields - Error handling: When conversion fails, returns
Noneand shows message (no crashes)
How These Skills Connect in Real Applications
Think of it like a professional workflow:
- Input arrives messy → User types
" JOHN DOE ","555-123-4567"," 25 " - Clean immediately →
strip()removes whitespace (ALWAYS do this first) - Transform format →
title()for names,lower()for emails, digit extraction for phones - Validate structure → Check length, check for required characters (
@), check numeric - Convert types → String
"25"becomes integer25for math/logic - Format output → F-strings present clean, professional results
- Handle errors gracefully → No crashes; helpful messages explain what's wrong
Critical Pattern: Clean → Validate → Convert → Use
This exact pattern appears in:
- Registration forms (exactly what you built!)
- User profile updates
- Payment processing (credit card numbers)
- Address validation
- Search queries
- API request validation
- Database input sanitization
Why This Matters
You didn't just learn string methods—you learned data quality engineering. Every application has the same problem:
Users provide messy, inconsistent, invalid data. Your job is to clean, validate, and standardize it before using it.
The Contact Card Validator demonstrates this principle. The skills you practiced here apply to every form, every database input, every API endpoint you'll ever build.
Next Steps: Extending Your Validator
Once your basic validator works, you can add real-world features:
Enhanced Validations:
- Email domain validation: Check if domain has
.(e.g.,[email protected]has.com) - Phone area code validation: Validate specific area codes (e.g.,
(555)is not a real US area code) - Name length limits: Enforce minimum/maximum name lengths
- International phone formats: Support formats from different countries
Additional Fields:
- Address validation: City, state, ZIP code
- Date of birth: Validate date format and calculate age automatically
- Username validation: Check length, allowed characters, no spaces
- Password strength: Check length, special characters, numbers
Data Storage (Chapter 22: File Handling):
- Save contacts to file: Store validated contacts in CSV or JSON format
- Load and edit: Read existing contacts and update them
- Search contacts: Find contacts by name or email
User Experience (Chapter 17: Control Flow):
- Retry on error: Let users re-enter invalid data instead of restarting
- Menu system: Choose to add, view, edit, or delete contacts
- Batch import: Validate multiple contacts at once from a file
Ask your AI companion how to add any of these features. Each one uses Chapter 16 concepts plus skills from upcoming chapters.
Try With AI
Setup
Use your preferred AI companion tool (Claude Code, Gemini CLI, or ChatGPT web chat). The prompts below progress from understanding the problem to building and extending the validator.
Prompt 1: Understand the Problem (Analyze)
I'm about to build a contact validator for a registration form. Real users will enter:
- Names with inconsistent capitalization and whitespace
- Emails in various formats (some invalid)
- Phone numbers with different formatting characters
- Ages that might not be numbers
What data quality problems should I anticipate? What validation rules are most important for each field?
Expected Outcome: You understand the problem domain before coding. This is professional development—knowing WHY you're validating, not just HOW.
Prompt 2: Build the Validator (Apply)
Help me build a Contact Card Validator that:
1. Collects name, email, phone, and age from user input
2. Cleans names (strips whitespace, title case, replaces underscores)
3. Validates email (must contain @ not at start/end, lowercase)
4. Formats phone numbers (removes formatting, validates 10 digits, displays as (XXX) XXX-XXXX)
5. Validates age (converts string to int, checks 0-120 range, determines adult status)
6. Displays a formatted contact card only if ALL fields are valid
7. Uses proper type hints (str, int, bool)
Show me complete, working Python code I can run.
Expected Outcome: You have a working validator that demonstrates all Chapter 16 concepts solving a real problem. Test it with the scenarios from Phase 3.
Prompt 3: Test Edge Cases (Validate)
My contact validator works for normal input, but I need to test edge cases:
Test these scenarios and tell me what should happen:
1. Name: " bob_SMITH " (spaces, underscores, mixed case)
2. Email: "user@" or "@example.com" (@ at edges)
3. Phone: "555-123-456" (only 9 digits) or "555-123-4567-8" (11 digits)
4. Age: "0", "120", "150", "twenty-five", "-5"
5. All fields empty (user just presses Enter)
For each case, show me what my validator should output and why.
Expected Outcome: You discover edge cases and learn defensive programming. Real applications must handle bad input gracefully.
Prompt 4: Reflect and Extend (Synthesize)
I've completed my contact validator. Looking back at Chapter 16:
1. Which string methods were most critical? (I'm thinking strip(), title(), replace(), find(), isdigit())
2. Why is "clean → validate → convert → use" the right pattern?
3. How would I add email domain validation (checking for "." in domain part)?
4. What would I need from future chapters to add features like:
- Retry on invalid input (Chapter 17: Control Flow)
- Save contacts to a file (Chapter 22: File Handling)
- Multiple contacts with a menu (Chapter 17: Loops)
Connect this project to real applications I'll build and explain why data validation matters in production systems.
Expected Outcome: You synthesize learning, connect to professional development, and see the path forward to more complex applications.
Prompt 5: Real-World Extension (Create)
I want to extend my contact validator with these features:
1. Email domain validation: Check that email contains "." after "@" (e.g., "[email protected]")
2. International phone support: Accept 11-digit numbers (country code) and format as +1 (XXX) XXX-XXXX
3. Validation summary: Show count of valid/invalid fields at the end
4. Detailed error messages: For each invalid field, explain exactly what's wrong and how to fix it
Show me how to add these features using only Chapter 16 concepts (strings, methods, f-strings, type casting).
Expected Outcome: You apply Chapter 16 concepts to new requirements independently. This is where learning becomes mastery.
Safety & Ethics Note: When using AI to help build your validator, remember:
- You design the validation rules, AI helps implement them
- Always test with realistic messy data before deploying
- If validation is too strict, legitimate users get frustrated
- If validation is too loose, bad data corrupts your database
- Your job is learning when to validate and when to be flexible
This project shows how professional development works: you solve a user problem (data quality), partner with AI for implementation, and validate with realistic data. Every application you build will use these exact skills.