Skip to content

Latest commit

 

History

History
1085 lines (853 loc) · 30.9 KB

File metadata and controls

1085 lines (853 loc) · 30.9 KB

Notes Application Implementation Plan

For Very Beginner Programmers

This plan will guide you through building a personal notes management application step-by-step. The approach is "start small, build outward" - each phase adds functionality while keeping the code clean and maintainable.


Core Principles

Clean Code Methods

  • Single Responsibility: Each function does ONE thing
  • Descriptive Names: Functions and variables clearly state their purpose
  • Small Functions: Keep functions short (ideally 10-20 lines)
  • No Magic Numbers: Use named constants instead of hardcoded values

HOME Directory Concept

The application will have a dedicated HOME directory for storing all notes, regardless of where the application is run from. This ensures:

  • Notes are always in the same location
  • The app works from any current working directory (CWD)
  • Easy backup and migration of all notes

Phase 0: Project Setup and Configuration

Goal

Set up the basic project structure and establish the HOME directory for notes.

Tasks

Task 0.1: Create Project Structure

PSEUDOCODE:
    CREATE directory structure:
        /src
            /notes          (core note functionality)
            /utils          (helper functions)
            /config         (configuration)
        /tests              (test files)
        /docs               (documentation)

Python Example:

notes-app/
├── src/
│   ├── __init__.py
│   ├── main.py
│   ├── notes/
│   │   └── __init__.py
│   ├── utils/
│   │   └── __init__.py
│   └── config/
│       └── __init__.py
└── tests/
    └── __init__.py

Java Example:

notes-app/
├── src/
│   └── main/
│       └── java/
│           └── com/
│               └── notes/
│                   ├── Main.java
│                   ├── notes/
│                   ├── utils/
│                   └── config/
└── test/
    └── java/

Task 0.2: Create Configuration Manager

PSEUDOCODE:
    DEFINE CONSTANT: DEFAULT_NOTES_HOME = "~/.notes" or "C:\Users\{user}\.notes"
    
    FUNCTION get_notes_home_directory():
        IF environment_variable "NOTES_HOME" exists:
            RETURN environment_variable_value
        ELSE:
            RETURN DEFAULT_NOTES_HOME
    
    FUNCTION ensure_notes_directory_exists():
        notes_home = get_notes_home_directory()
        IF directory does NOT exist:
            CREATE directory at notes_home
        RETURN notes_home

Why? This ensures all notes are stored in one place, no matter where you run the app from.

Task 0.3: Create Path Utilities

PSEUDOCODE:
    FUNCTION get_absolute_path_to_notes_home():
        notes_home = get_notes_home_directory()
        RETURN convert_to_absolute_path(notes_home)
    
    FUNCTION build_note_file_path(note_filename):
        notes_home = get_absolute_path_to_notes_home()
        RETURN join_paths(notes_home, note_filename)

Test:

  • Run from different directories and verify notes are always stored in the same location
  • Set NOTES_HOME environment variable and verify it's used

Phase 1: Core Data Structure - The Note Object

Goal

Create a simple representation of a note with metadata.

Tasks

Task 1.1: Define Note Data Structure

PSEUDOCODE:
    CLASS Note:
        PROPERTIES:
            title (string)
            content (string)
            created_timestamp (datetime)
            modified_timestamp (datetime)
            tags (list of strings)
            author (string, optional)
            status (string, optional)
            priority (integer, optional, 1-5)
        
        CONSTRUCTOR(title, content):
            SET this.title = title
            SET this.content = content
            SET this.created_timestamp = current_datetime()
            SET this.modified_timestamp = current_datetime()
            SET this.tags = empty_list
            SET this.author = None
            SET this.status = None
            SET this.priority = None

Why? Having a clear data structure makes everything else easier to build.

Task 1.2: Add Note Validation

PSEUDOCODE:
    FUNCTION is_valid_note(note):
        IF note.title is empty OR title is only whitespace:
            RETURN False
        IF note.content is None:
            RETURN False
        RETURN True
    
    FUNCTION validate_title(title):
        IF title is empty OR only whitespace:
            THROW error "Title cannot be empty"
        IF length of title > 200:
            THROW error "Title too long (max 200 characters)"
        RETURN True

Test:

  • Create note with valid title → should succeed
  • Create note with empty title → should fail
  • Create note with very long title → should fail

Phase 2: File Operations - Reading and Writing Notes

Goal

Learn to save and load notes as text files with YAML headers.

Tasks

Task 2.1: Generate Unique Note Filename

PSEUDOCODE:
    FUNCTION generate_note_filename(title):
        // Sanitize title to be filesystem-safe
        safe_title = remove_special_characters(title)
        safe_title = replace_spaces_with_dashes(safe_title)
        safe_title = convert_to_lowercase(safe_title)
        
        // Add timestamp for uniqueness
        timestamp = get_current_timestamp_string()  // e.g., "20250520-143022"
        
        filename = safe_title + "-" + timestamp + ".md"
        RETURN filename

Example: "My First Note" → "my-first-note-20250520-143022.md"

Task 2.2: Format Note as YAML + Markdown

PSEUDOCODE:
    FUNCTION format_note_for_file(note):
        // Start with YAML front matter delimiter
        output = "---\n"
        
        // Add required fields
        output = output + "title: " + note.title + "\n"
        output = output + "created: " + format_iso8601(note.created_timestamp) + "\n"
        output = output + "modified: " + format_iso8601(note.modified_timestamp) + "\n"
        
        // Add optional fields if present
        IF note.tags is not empty:
            output = output + "tags: [" + join_with_comma(note.tags) + "]\n"
        IF note.author is not None:
            output = output + "author: " + note.author + "\n"
        IF note.status is not None:
            output = output + "status: " + note.status + "\n"
        IF note.priority is not None:
            output = output + "priority: " + note.priority + "\n"
        
        // Close YAML front matter
        output = output + "---\n\n"
        
        // Add content
        output = output + note.content
        
        RETURN output

Why? This creates a standard format that's both human-readable and machine-parsable.

Task 2.3: Save Note to File

PSEUDOCODE:
    FUNCTION save_note(note):
        // Validate note
        IF NOT is_valid_note(note):
            THROW error "Invalid note"
        
        // Generate filename
        filename = generate_note_filename(note.title)
        
        // Get full path
        full_path = build_note_file_path(filename)
        
        // Format note content
        file_content = format_note_for_file(note)
        
        // Write to file
        OPEN file at full_path for writing:
            WRITE file_content to file
        
        RETURN filename

Test:

  • Save a note and verify the file exists
  • Open the file in a text editor and verify format
  • Verify file is in the NOTES_HOME directory

Task 2.4: Parse YAML Header

PSEUDOCODE:
    FUNCTION parse_yaml_header(file_content):
        // Split content by YAML delimiters
        IF file_content does NOT start with "---":
            THROW error "Invalid note format: missing YAML header"
        
        // Find the closing delimiter
        lines = split_by_newline(file_content)
        yaml_end_index = find_second_occurrence_of("---", lines)
        
        IF yaml_end_index is -1:
            THROW error "Invalid note format: YAML header not closed"
        
        // Extract YAML section
        yaml_lines = lines[1 to yaml_end_index]
        yaml_text = join_with_newline(yaml_lines)
        
        // Parse YAML (use library)
        metadata = parse_yaml(yaml_text)
        
        // Extract content (everything after second ---)
        content_lines = lines[yaml_end_index + 1 onwards]
        content = join_with_newline(content_lines)
        content = trim_whitespace(content)
        
        RETURN (metadata, content)

Why? Separating the parsing logic makes it reusable and testable.

Task 2.5: Load Note from File

PSEUDOCODE:
    FUNCTION load_note(filename):
        // Get full path
        full_path = build_note_file_path(filename)
        
        // Check if file exists
        IF file does NOT exist at full_path:
            THROW error "Note file not found: " + filename
        
        // Read file
        OPEN file at full_path for reading:
            file_content = READ entire file
        
        // Parse YAML and content
        (metadata, content) = parse_yaml_header(file_content)
        
        // Create Note object
        note = CREATE new Note(metadata.title, content)
        note.created_timestamp = parse_iso8601(metadata.created)
        note.modified_timestamp = parse_iso8601(metadata.modified)
        
        IF metadata contains tags:
            note.tags = metadata.tags
        IF metadata contains author:
            note.author = metadata.author
        IF metadata contains status:
            note.status = metadata.status
        IF metadata contains priority:
            note.priority = metadata.priority
        
        RETURN note

Test:

  • Save a note, then load it back
  • Verify all fields match
  • Try loading a non-existent file → should get error

Phase 3: Basic CRUD Operations

Goal

Implement Create, Read, Update, Delete operations for notes.

Tasks

Task 3.1: Create Note

PSEUDOCODE:
    FUNCTION create_note(title, content, tags=None):
        // Create new Note object
        note = CREATE new Note(title, content)
        
        // Add tags if provided
        IF tags is not None:
            note.tags = tags
        
        // Save to file
        filename = save_note(note)
        
        PRINT "Note created successfully: " + filename
        RETURN filename

Test:

  • Create several notes with different titles
  • Verify each is saved as a separate file

Task 3.2: List All Notes

PSEUDOCODE:
    FUNCTION list_all_notes():
        // Get notes directory
        notes_dir = get_absolute_path_to_notes_home()
        
        // Get all .md files
        all_files = list_files_in_directory(notes_dir)
        note_files = filter_by_extension(all_files, ".md")
        
        // Sort by modification time (newest first)
        note_files = sort_by_modified_time(note_files, descending=True)
        
        RETURN note_files
    
    FUNCTION display_notes_list(note_files):
        IF note_files is empty:
            PRINT "No notes found."
            RETURN
        
        PRINT "Your Notes:"
        PRINT "============"
        
        FOR EACH filename IN note_files:
            // Load just the metadata (don't need full content)
            note = load_note(filename)
            
            PRINT filename + ":"
            PRINT "  Title: " + note.title
            PRINT "  Modified: " + format_readable_date(note.modified_timestamp)
            IF note.tags is not empty:
                PRINT "  Tags: " + join_with_comma(note.tags)
            PRINT ""

Test:

  • Create 3 notes
  • List them and verify all appear
  • Verify they're sorted by modification time

Task 3.3: Read Specific Note

PSEUDOCODE:
    FUNCTION read_note_by_filename(filename):
        note = load_note(filename)
        display_note(note)
    
    FUNCTION display_note(note):
        PRINT "=" * 50
        PRINT note.title
        PRINT "=" * 50
        PRINT ""
        PRINT "Created: " + format_readable_date(note.created_timestamp)
        PRINT "Modified: " + format_readable_date(note.modified_timestamp)
        
        IF note.tags is not empty:
            PRINT "Tags: " + join_with_comma(note.tags)
        
        IF note.author is not None:
            PRINT "Author: " + note.author
        
        PRINT ""
        PRINT "-" * 50
        PRINT note.content
        PRINT "-" * 50

Test:

  • Create a note
  • Read it back and verify all content displays correctly

Task 3.4: Update Note

PSEUDOCODE:
    FUNCTION update_note(filename, new_content=None, new_tags=None):
        // Load existing note
        note = load_note(filename)
        
        // Update fields
        IF new_content is not None:
            note.content = new_content
        
        IF new_tags is not None:
            note.tags = new_tags
        
        // Update modification timestamp
        note.modified_timestamp = current_datetime()
        
        // Save (overwrites existing file)
        full_path = build_note_file_path(filename)
        file_content = format_note_for_file(note)
        
        OPEN file at full_path for writing:
            WRITE file_content to file
        
        PRINT "Note updated successfully: " + filename

Test:

  • Create a note
  • Update its content
  • Load it again and verify changes

Task 3.5: Delete Note

PSEUDOCODE:
    FUNCTION delete_note(filename):
        full_path = build_note_file_path(filename)
        
        IF file does NOT exist at full_path:
            THROW error "Note not found: " + filename
        
        // Ask for confirmation
        PRINT "Are you sure you want to delete '" + filename + "'? (yes/no): "
        confirmation = READ user_input()
        
        IF confirmation equals "yes":
            DELETE file at full_path
            PRINT "Note deleted successfully: " + filename
        ELSE:
            PRINT "Deletion cancelled."

Test:

  • Create a note
  • Delete it
  • Verify the file is gone
  • Try to delete again → should get error

Phase 4: Search and Filter

Goal

Add ability to find notes by keywords, tags, or date ranges.

Tasks

Task 4.1: Search by Keyword in Content

PSEUDOCODE:
    FUNCTION search_notes_by_keyword(keyword):
        all_note_files = list_all_notes()
        matching_notes = empty_list
        
        FOR EACH filename IN all_note_files:
            note = load_note(filename)
            
            // Search in title and content (case-insensitive)
            IF keyword.lower() in note.title.lower():
                ADD (filename, note) to matching_notes
            ELSE IF keyword.lower() in note.content.lower():
                ADD (filename, note) to matching_notes
        
        RETURN matching_notes
    
    FUNCTION display_search_results(results, keyword):
        IF results is empty:
            PRINT "No notes found containing '" + keyword + "'"
            RETURN
        
        PRINT "Found " + count(results) + " note(s) containing '" + keyword + "':"
        PRINT ""
        
        FOR EACH (filename, note) IN results:
            PRINT filename + ": " + note.title

Test:

  • Create notes with specific words
  • Search for those words
  • Verify correct notes are found

Task 4.2: Filter by Tag

PSEUDOCODE:
    FUNCTION filter_notes_by_tag(tag):
        all_note_files = list_all_notes()
        matching_notes = empty_list
        
        FOR EACH filename IN all_note_files:
            note = load_note(filename)
            
            // Check if tag exists in note's tags (case-insensitive)
            IF tag.lower() in [t.lower() for t in note.tags]:
                ADD (filename, note) to matching_notes
        
        RETURN matching_notes

Test:

  • Create notes with different tags
  • Filter by a specific tag
  • Verify only notes with that tag appear

Task 4.3: Get All Unique Tags

PSEUDOCODE:
    FUNCTION get_all_tags():
        all_note_files = list_all_notes()
        all_tags = empty_set  // Use set to avoid duplicates
        
        FOR EACH filename IN all_note_files:
            note = load_note(filename)
            FOR EACH tag IN note.tags:
                ADD tag to all_tags
        
        // Convert to sorted list
        RETURN sort_alphabetically(list(all_tags))

Test:

  • Create notes with various tags
  • Get all tags
  • Verify no duplicates and proper sorting

Phase 5: Command Line Interface

Goal

Create a user-friendly command-line interface to interact with notes.

Tasks

Task 5.1: Implement Command Parser

PSEUDOCODE:
    FUNCTION parse_command_line_arguments(args):
        IF args is empty OR args[0] equals "--help":
            display_help()
            RETURN
        
        command = args[0]
        
        SWITCH command:
            CASE "create":
                handle_create_command(args[1:])
            CASE "list":
                handle_list_command(args[1:])
            CASE "read":
                handle_read_command(args[1:])
            CASE "update":
                handle_update_command(args[1:])
            CASE "delete":
                handle_delete_command(args[1:])
            CASE "search":
                handle_search_command(args[1:])
            CASE "tags":
                handle_tags_command(args[1:])
            DEFAULT:
                PRINT "Unknown command: " + command
                PRINT "Use --help for usage information"

Task 5.2: Implement Help Command

PSEUDOCODE:
    FUNCTION display_help():
        PRINT "Notes Application - Personal Note Manager"
        PRINT ""
        PRINT "Usage:"
        PRINT "  notes create [--tags tag1,tag2]     Create a new note"
        PRINT "  notes list [--tag tagname]          List all notes or filter by tag"
        PRINT "  notes read <filename>               Display a specific note"
        PRINT "  notes update <filename>             Update a note"
        PRINT "  notes delete <filename>             Delete a note"
        PRINT "  notes search <keyword>              Search notes by keyword"
        PRINT "  notes tags                          List all tags"
        PRINT "  notes --help                        Show this help message"
        PRINT ""
        PRINT "Environment Variables:"
        PRINT "  NOTES_HOME    Directory where notes are stored (default: ~/.notes)"

Task 5.3: Implement Create Command Handler

PSEUDOCODE:
    FUNCTION handle_create_command(args):
        // Parse arguments
        tags = extract_tags_from_args(args)  // Look for --tags flag
        
        // Get title from user
        PRINT "Enter note title: "
        title = READ user_input()
        
        IF title is empty:
            PRINT "Error: Title cannot be empty"
            RETURN
        
        // Get content from user (multi-line)
        PRINT "Enter note content (press Ctrl+D or Ctrl+Z when done):"
        content = READ multi_line_input()
        
        // Create the note
        filename = create_note(title, content, tags)
        PRINT "Note created: " + filename

Alternative: Open a text editor for content entry:

PSEUDOCODE:
    FUNCTION handle_create_command_with_editor(args):
        tags = extract_tags_from_args(args)
        
        PRINT "Enter note title: "
        title = READ user_input()
        
        // Create temporary file
        temp_file = create_temporary_file()
        
        // Open in default editor
        editor = get_environment_variable("EDITOR") OR "nano"
        EXECUTE command: editor + " " + temp_file
        
        // Read content from temp file
        content = READ temp_file
        
        // Create note
        filename = create_note(title, content, tags)
        
        // Clean up
        DELETE temp_file
        
        PRINT "Note created: " + filename

Task 5.4: Implement List Command Handler

PSEUDOCODE:
    FUNCTION handle_list_command(args):
        // Check if filtering by tag
        tag_filter = extract_tag_filter_from_args(args)  // Look for --tag flag
        
        IF tag_filter is not None:
            results = filter_notes_by_tag(tag_filter)
            PRINT "Notes tagged with '" + tag_filter + "':"
            FOR EACH (filename, note) IN results:
                PRINT "  " + filename + ": " + note.title
        ELSE:
            note_files = list_all_notes()
            display_notes_list(note_files)

Task 5.5: Implement Other Command Handlers

PSEUDOCODE:
    FUNCTION handle_read_command(args):
        IF length of args < 1:
            PRINT "Error: Please specify a filename"
            PRINT "Usage: notes read <filename>"
            RETURN
        
        filename = args[0]
        read_note_by_filename(filename)
    
    FUNCTION handle_search_command(args):
        IF length of args < 1:
            PRINT "Error: Please specify a search keyword"
            PRINT "Usage: notes search <keyword>"
            RETURN
        
        keyword = args[0]
        results = search_notes_by_keyword(keyword)
        display_search_results(results, keyword)
    
    FUNCTION handle_tags_command(args):
        all_tags = get_all_tags()
        
        IF all_tags is empty:
            PRINT "No tags found."
        ELSE:
            PRINT "All tags:"
            FOR EACH tag IN all_tags:
                PRINT "  - " + tag

Task 5.6: Create Main Entry Point

PSEUDOCODE:
    FUNCTION main():
        // Ensure notes directory exists
        ensure_notes_directory_exists()
        
        // Get command line arguments
        args = get_command_line_arguments()
        
        // Parse and execute command
        TRY:
            parse_command_line_arguments(args)
        CATCH any error as e:
            PRINT "Error: " + error_message(e)
            PRINT "Use --help for usage information"
            EXIT with error code 1

Test:

  • Run each command and verify it works
  • Try invalid commands and verify error messages
  • Test with and without optional flags

Phase 6: Error Handling and Edge Cases

Goal

Make the application robust by handling errors gracefully.

Tasks

Task 6.1: Handle File System Errors

PSEUDOCODE:
    FUNCTION safe_file_operation(operation_function):
        TRY:
            RETURN operation_function()
        CATCH FileNotFoundError:
            PRINT "Error: File not found"
            RETURN None
        CATCH PermissionError:
            PRINT "Error: Permission denied. Check file permissions."
            RETURN None
        CATCH IOError as e:
            PRINT "Error: Failed to access file - " + error_message(e)
            RETURN None

Task 6.2: Validate User Input

PSEUDOCODE:
    FUNCTION get_validated_input(prompt, validator_function):
        WHILE True:
            PRINT prompt
            user_input = READ user_input()
            
            IF validator_function(user_input):
                RETURN user_input
            ELSE:
                PRINT "Invalid input. Please try again."
    
    FUNCTION is_valid_title(title):
        RETURN title is not empty AND length(title) <= 200
    
    FUNCTION is_valid_tag(tag):
        // Tags should be single words, no spaces
        RETURN NOT contains_whitespace(tag) AND length(tag) > 0

Task 6.3: Handle Corrupted Files

PSEUDOCODE:
    FUNCTION load_note_safely(filename):
        TRY:
            RETURN load_note(filename)
        CATCH YAMLParseError:
            PRINT "Warning: File '" + filename + "' has corrupted YAML header"
            PRINT "Would you like to view the raw content? (yes/no): "
            response = READ user_input()
            
            IF response equals "yes":
                raw_content = read_file(build_note_file_path(filename))
                PRINT raw_content
            
            RETURN None
        CATCH any error as e:
            PRINT "Error loading note: " + error_message(e)
            RETURN None

Phase 7: Advanced Features (Optional Enhancements)

Goal

Add nice-to-have features that improve usability.

Tasks

Task 7.1: Note Statistics

PSEUDOCODE:
    FUNCTION display_statistics():
        all_note_files = list_all_notes()
        
        total_notes = count(all_note_files)
        all_tags = get_all_tags()
        total_tags = count(all_tags)
        
        total_words = 0
        FOR EACH filename IN all_note_files:
            note = load_note(filename)
            total_words = total_words + count_words(note.content)
        
        PRINT "Notes Statistics:"
        PRINT "================="
        PRINT "Total notes: " + total_notes
        PRINT "Total tags: " + total_tags
        PRINT "Total words: " + total_words
        PRINT "Average words per note: " + (total_words / total_notes)

Task 7.2: Export Note

PSEUDOCODE:
    FUNCTION export_note_to_html(filename, output_path):
        note = load_note(filename)
        
        html_content = "<!DOCTYPE html>\n"
        html_content = html_content + "<html>\n<head>\n"
        html_content = html_content + "<title>" + note.title + "</title>\n"
        html_content = html_content + "</head>\n<body>\n"
        html_content = html_content + "<h1>" + note.title + "</h1>\n"
        html_content = html_content + "<p><small>Created: " + note.created_timestamp + "</small></p>\n"
        html_content = html_content + "<hr>\n"
        html_content = html_content + convert_markdown_to_html(note.content)
        html_content = html_content + "</body>\n</html>"
        
        WRITE html_content to output_path
        PRINT "Note exported to: " + output_path

Task 7.3: Backup All Notes

PSEUDOCODE:
    FUNCTION backup_all_notes(backup_directory):
        notes_dir = get_absolute_path_to_notes_home()
        timestamp = format_timestamp_for_filename(current_datetime())
        backup_name = "notes-backup-" + timestamp + ".zip"
        
        backup_path = join_paths(backup_directory, backup_name)
        
        CREATE zip_file at backup_path:
            ADD all files from notes_dir to zip_file
        
        PRINT "Backup created: " + backup_path

Testing Strategy

Unit Tests to Write

For each major function, write at least 3-4 tests:

  1. Happy Path: Test with valid input
  2. Edge Cases: Empty strings, very long strings, special characters
  3. Error Cases: Invalid input, missing files, corrupted data
  4. Boundary Conditions: Maximum lengths, minimum values

Example Test Cases

PSEUDOCODE for tests:

TEST create_note_with_valid_data:
    note = create_note("Test Title", "Test content")
    ASSERT note.title equals "Test Title"
    ASSERT note.content equals "Test content"
    ASSERT note.created_timestamp is not None

TEST create_note_with_empty_title_should_fail:
    EXPECT error when create_note("", "Some content")

TEST save_and_load_note_preserves_data:
    original = create_note("My Note", "Content here", ["tag1", "tag2"])
    filename = save_note(original)
    loaded = load_note(filename)
    ASSERT loaded.title equals original.title
    ASSERT loaded.content equals original.content
    ASSERT loaded.tags equals original.tags

TEST list_notes_returns_all_files:
    create_note("Note 1", "Content 1")
    create_note("Note 2", "Content 2")
    create_note("Note 3", "Content 3")
    notes = list_all_notes()
    ASSERT count(notes) >= 3

TEST search_finds_matching_notes:
    create_note("Python Tutorial", "Learn Python programming")
    create_note("Java Guide", "Learn Java programming")
    results = search_notes_by_keyword("Python")
    ASSERT count(results) >= 1
    ASSERT "Python Tutorial" in results[0].title

Implementation Order Summary

Follow this order to build the application incrementally:

Week 1: Foundation

  1. Set up project structure (Task 0.1)
  2. Implement configuration and path utilities (Tasks 0.2-0.3)
  3. Define Note data structure (Task 1.1)
  4. Add basic validation (Task 1.2)

Week 2: File Operations

  1. Implement filename generation (Task 2.1)
  2. Implement YAML formatting (Task 2.2)
  3. Implement save function (Task 2.3)
  4. Implement YAML parsing (Task 2.4)
  5. Implement load function (Task 2.5)

Week 3: CRUD Operations

  1. Implement create (Task 3.1)
  2. Implement list (Task 3.2)
  3. Implement read (Task 3.3)
  4. Implement update (Task 3.4)
  5. Implement delete (Task 3.5)

Week 4: Search Features

  1. Implement keyword search (Task 4.1)
  2. Implement tag filtering (Task 4.2)
  3. Implement get all tags (Task 4.3)

Week 5: CLI Interface

  1. Implement command parser (Task 5.1)
  2. Implement help command (Task 5.2)
  3. Implement all command handlers (Tasks 5.3-5.5)
  4. Create main entry point (Task 5.6)

Week 6: Polish and Testing

  1. Add error handling (Tasks 6.1-6.3)
  2. Write comprehensive tests
  3. Optional: Add advanced features (Phase 7)

Language-Specific Notes

Python Implementation Tips

Libraries to Use:

  • os and pathlib for file operations
  • PyYAML for YAML parsing
  • datetime for timestamps
  • argparse for command-line argument parsing

Example Setup:

# Install dependencies
pip install pyyaml

# Project structure matches Phase 0

Java Implementation Tips

Libraries to Use:

  • java.nio.file for file operations
  • org.yaml.snakeyaml for YAML parsing (add to Maven/Gradle)
  • java.time for timestamps
  • Built-in argument parsing or Apache Commons CLI

Example Setup:

<!-- Maven dependency for YAML -->
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>2.0</version>
</dependency>

Clean Code Checklist

Before considering any task complete, verify:

  • Function names clearly describe what they do
  • Each function has a single, clear purpose
  • No magic numbers (use named constants)
  • Error cases are handled gracefully
  • User-facing messages are clear and helpful
  • Code is commented where logic is complex
  • Tests cover the main use cases
  • No hardcoded file paths (use configuration)

Debugging Tips

When something doesn't work:

  1. Print/Log Everything: Add print statements to see what values variables have
  2. Test Small Pieces: Don't write 100 lines before testing
  3. Use the REPL: Test individual functions in Python's interactive shell or JShell
  4. Read Error Messages Carefully: They usually tell you exactly what's wrong
  5. Check File Paths: Make sure files are where you think they are
  6. Verify Permissions: Ensure you can read/write in the notes directory

Next Steps After CLI

Once you have a working CLI application:

  1. Add More Commands: Export, import, archive old notes
  2. Improve Search: Add date range filtering, regex support
  3. Add Configuration File: Let users customize default settings
  4. Build GUI (Phase 2): Use Tkinter (Python) or Swing (Java)
  5. Build Web Server (Phase 3): Use Flask/Django (Python) or Spring Boot (Java)

Congratulations!

By following this plan, you will have built a complete, working application while learning:

  • File I/O and data persistence
  • Data structures and validation
  • Command-line interface design
  • Error handling
  • Test-driven development
  • Clean code principles
  • Project organization

Remember: Start small, test often, build incrementally!